Professional Documents
Culture Documents
OOP Predavanje 07 White
OOP Predavanje 07 White
programiranje
C++ continued
Razmišljanje u okvirima objekata
2
C++
Podsjetimo se!
▪ C++ sintaksa za definiranje klasa je slična sintaksi struktura
C++
definiranje klasa
▪ C++ sintaksa za definiranje klasa je slična sintaksi struktura
class Time{
// Privatne varijable
private:
int hour;
int minute;
int second;
// Public metode
public:
Time();
~Time();
void Set(const int Hr, const int Min, const int Sec);
void Get(int &Hr, int &Min, int &Sec) const;
void Print() const;
}; const?
C++
implementiranje klasa
▪ Metode se implementiraju poput običnih funkcija
▪ Dodajemo ime klase prije imena metode
C++
implementiranje klasa
▪ Podsjetimo se: konstruktor i destruktor nemaju povratne
vrijednosti
▪ Čak ni void
Time::Time(){
cout << "Constructor\n";
}
Time::~Time(){
cout << "Destructor\n";
}
void Time::Set(const int Hr, const int Min, const int Sec){
cout << "Set\n";
hour = Hr;
minute = Min;
second = Sec;
}
C++
korištenje klasa
• Objekte deklariramo kao i obične varijable
class_name object_name;
• Ovime ćemo alocirati djelić memorije dovoljno velik da sadrži
sve podatke unutar klase
• Također, automatski će se pozvati i default konstruktor
C++
napredne klase
▪ Pridjeljivanje objekata
– Pretpostavimo da su Now i Then objekti iste klase
– MOŽEMO koristiti "Now = Then;"
– Program će izvršiti field-by-field kopiranje objekta
C++
napredne klase
▪ Ispisivanje objekata
– NE SMIJEMO ispisivati objekt korištenjem „cout << Now”
– Moramo koristiti vlastitu metodu klase
– Npr. Now.Print() koju smo sami implementirali
▪ Inicijaliziranje objekta
– Možemo postaviti inicijalne vrijednosti prilikom deklariranja
objekta
– Za to trebamo kreirati konstruktor koji prima dodatne
parametre
C++
napredne klase
▪ Objekti kao parametri
– Objekte možemo slati kao parametre preko vrijednosti ili
preko reference
– Slanje preko reference je malo brže jer ne zahtijeva kopiranje
objekata
C++
napredne klase
▪ Metode smiju biti private
▪ što to znači? kad se koristi?
▪ Podaci smiju biti public
▪ ovime se ipak razbija koncept skrivanja podataka u OOP
C++
napredne klase
▪ Nizovi objekata
▪ Slobodno možemo koristiti nizove za spremanje objekata
C++
osnovni (default) konstruktor
▪ Osnovni konstruktor je metoda koja se poziva automatski
prilikom stvaranja objekata
▪ Osnovni konstruktor nema parametre i postavlja sve
podatke u neku inicijalnu vrijednost
class_name();
▪ Na sljedeći način se kreira objekt klase:
class_name object;
C++
overload-ani konstruktor
▪ Možemo kreirati konstruktor koji prima parametre
C++
kopirni (copy) konstruktor
▪ Riječ je o posebnoj metodi koja kreira novi objekt tako da
kopira drugi objekt
▪ Ova se metoda automatski poziva kad šaljemo objekt u
funkciju preko vrijednosti
▪ Copy konstruktor mora primiti jedan objekt poslan preko
const reference
C++
copy konstruktor
▪ copy konstruktor se također može koristiti za stvaranje objekata
class ShalloC{
private:
int * x; Konstruktor s jednim parametrom
public:
ShalloC(int m){ int main(){
x = new int;
*x = m;
ShalloC ob1(10);
} ShalloC ob2 = ob1 ;
int GetX() const{ ob1.PrintX();
return *x; ob2.PrintX();
}
void SetX(int m){
*x = m;
ob1.SetX(12);
} ob1.PrintX();
void PrintX(){ ob2.PrintX();
cout << "Int X=" << *x << endl; }
}
~ShalloC(){
delete x;
}
};
Oba objekta imaju x
postavljen u 12 !
class ShalloC{
private:
shallow vs. deep copy 22
int * x;
public:
ShalloC(int m){
x = new int; copy konstruktor i obavljanje
*x = m; deep copy-ja (duboko kopiranje)
}
ShalloC(const ShalloC& obj){
x = new int;
*x = obj.GetX(); int main(){
} ShalloC ob1(10);
int GetX() const{
return *x;
ShalloC ob2 = ob1 ;
} ob1.PrintX();
void SetX(int m){ ob2.PrintX();
*x = m;
} ob1.SetX(12);
void PrintX(){
cout << "Int X=" << *x << endl;
ob1.PrintX();
} ob2.PrintX();
~ShalloC(){ }
delete x;
}
};
C++
static
class family
{
public: Čemu služi ključna riječ static?
…
private:
static const int NUM_CHILDREN = 19;
string mother, father;
string children[NUM_CHILDREN];
…
};
Uvod
• Kao i u stvarnom svijetu, u programiranju ne postoji samo
jedan pristup rješavanju problema
• Model rješenja ne mora biti savršen u prvoj iteraciji
• Najbolje istražiti više različitih pristupa
• U startu procesa nije još bitan odabir programskog jezika
• Početak je rješavanje problema/zadatka domene
• Odabir tehnologije u startu samo ako je fundamentalno
vezana za sam problem
OO programiranje
• Osim objekata u OO pristupu koristimo i različite programske
strukture (petlje, uvjetne izraze, itd.)
• Za početak programiranja u OO pristupu potrebno je prvo
naučiti OO koncepte i OO način razmišljanja
• Tri važne stvari kod OO načina razmišljanja:
– Poznavanje razlike između sučelja i implementacije
– Apstraktno razmišljanje
– Pružanje korisniku samo minimalno potrebno sučelje
Sučelje vs implementacija
• Kod dizajna klase potrebno je razlučiti što korisnik treba, a što
ne treba znati
• Mehanizam skrivanja podataka sadržan u procesu
učahurivanja
Sučelje (interface)
• Usluge koje se pružaju korisniku zajedno čine sučelje
• U najboljem slučaju samo one usluge koje su korisniku
potrebne idu u sučelje
• Pravo pitanje je što korisniku zapravo treba
• Različiti ljudi će vjerojatno dati donekle različite odgovore na
ovo pitanje
• Potrebno je poznavati korisnike klase koje dizajniramo i na koji
način će one njima biti korisne
Implementacija
• Detalji implementacije su skriveni od korisnika
• Jedna bitna stvar koju treba imati na umu: promjene u
implementaciji ne bi trebale utjecati na kod koji je korisnik
pisao, niti zahtijevati da korisnik mora mijenjati svoj kod
• Interfejs sadrži sintaksu kako pozvati metodu i povratnu
vrijednost – ako se ovo ne mijenja i dobije se ista povratna
vrijednost onda korisnik ne mora ništa mijenjati
• Primjer korištenja mobitela – interfejs je jednostavan –
utipkamo broj i pozovemo. U slučaju da se implementacija
mijenja (npr. operater instalira novu opremu) to ne mijenja
način na koji mi koristimo mobitel
Primjer
• Potrebno je napraviti klasu kora predstavlja čitač iz baze
podataka
• Potrebno je poznavati koje funkcionalnosti treba naš korisnik
(korisnik ove klase)
• Obično se zahtjevi navedu u dokumentu vezanom za projekt
nakon analize i razgovora sa potencijalnim korisnicima
Primjer
• Klasa koju dizajniramo je namijenjena programerima kojima je
potrebna upotreba baze podataka
• Prema tome, ovo sučelje je zapravo API – Application-
programming interface koje će korisnik koristiti
• Ovakav interfejs je zapravo wrapper funkcionalnosti baze
• Razlog zbog kojeg ga radimo je mogućnost prilagodbe
(customization) različitih funkcionalnosti (npr. dodatna
priprema podataka prije zapisa ili čitanja iz baze)
• Dodatan razlog je mogućnost promjene sustava/baze bez
potrebe za mijenjanjem korisničkog koda
Lista zahtjeva
• Otvaranje konekcije prema bazi
• Zatvaranje konekcije prema bazi
• Postavi pokazivač na poziciju prvog zapisa
• Postavi pokazivač na poziciju zadnjeg zapisa
• Dohvati ukupan broj zapisa
• Da li ima još zapisa (da li je pokazivač na zadnjem zapisu)
• Postavi pokazivač na određeni zapis korištenjem ključa
• Dohvati određeni zapis korištenjem ključa
• Dohvati sljedeći zapis u odnosu na pokazivač
Primjer
ČitačBazePodataka
+otvori:void
+zatvori:void
+idiNaPrviElement:void
+idiNaZadnjiElement:void
+dohvatiBrojElemenata:int
+pročitajZapis:String
Interface
• Za svaki od zahtjeva koji smo napisali također smo napisali i
metodu koja pruža tu funkcionalnost. Potrebno je postaviti
nekoliko pitanja:
• Da bi korisnik (programer) efektivno koristio klasu je li
potrebno da zna još nešto o njoj?
• Treba li korisnik znati interni kod implementiran za otvaranje
baze podataka?
• Treba li korisnik znati interni kod implementiran za
postavljanje pokazivača na neki određeni zapis?
• Treba li korisnik znati interni kod implementiran za provjeru
postoji li još zapisa?
Interface
• Odgovor na ova pitanja je ne
• Nije potrebno poznavati ove informacije
• Ono što je bitno da korisnik dobije točne povratne vrijednosti i da
su operacije ispravno napravljene
• Aplikacijski programer će zapravo najvjerojatnije biti još jedan
apstraktni nivo iznad implementacije
• Aplikacija će koristiti ove klase za otvoriti bazu, a klase će pozvati
API od baze podataka
• Da bi se pružio minimalni interfejs najbolje je krenuti od potpuno
praznog interfejsa i polako dodavati funkcionalnosti koje bi korisnik
želio
• Nije dobro unaprijed pretpostavljati će korisnik željeti neku
specifičnu funkcionalnost
Interface
• Stvaranje wrappera može izgledati kao suvišan posao, ali ima svojih
prednosti
• Npr. razmotrimo potrebu mapiranja objekata u relacijske baze
podataka
• Na tržištu postoje OO baze podataka, ali i veliki broj sustava koji
koristi relacijske baze
• Ako radimo programe za sustav koji koristi relacijski model može se
koristiti middleware koji će mapirati objekte iz aplikacijskog koda u
relacijski model
• Čak i ako iz početka stvorimo potpuni OO sustav često nećemo moći
izbjeći korištenje relacijskih baza – budući da većina sustava ovisi o
informacijama iz nekog drugog ili više različitih sustava
Object persistence
• Koncept koji se odnosi na spremanje stanja objekta na način
koji omogućava njegovo kasnije korištenje
• Objekt koji ne koristi „persistnace” po prirodi nestaje kada
izađe iz dosega
• Npr. stanje objekta se može spremiti u bazu podataka ili
datoteku
Primjer implementacije
public void open(String ime){
/*Kod potreban za specifične zadatke*/
/*Pozovi Oracle API za otvaranje baze*/
/*Dodatni kod potreban za specifične zadatke*/
}
Primjer implementacije
public void open(String ime){
/*Kod potreban za specifične zadatke*/
/*Pozovi SQLAnywhere API za otvaranje baze*/
/*Dodatni kod potreban za specifične zadatke*/
}
Primjer implementacije
Apstraktno razmišljanje
• Jedna od najvećih prednosti OO programiranja je mogućnost
ponovnog korištenja koda (eng. code reuse)
• Konkretni interfejsi su vrlo specifični, a apstraktni interfejsi su
generalniji
• Primjer razlike između apstraktnog i konkretnog interfejsa
Odvedi me
na aerodrom
Apstraktno razmišljanje
• Postavlja se pitanje koji od ova dva scenarija ima bolju
mogućnost ponovnog korištenja u različitim situacijama
Vozi ravno,
skreni desno,
ravno, lijevo …
Minimalni interface
• Pružite korisniku isključivo samo ono što mu je potrebno. Ako
pokaže da je potrebno proširiti interfejs to se radi iterativno.
• Bitno je klase dizajnirati iz perspektive korisnika, a ne iz
perspektive sustava. Dizajneri klasa klase dizajniraju na način
da se uklapa u specifični tehnološki model. Potrebno je
razmišljati na koji način će ta klasa biti korisna korisniku.
Potrebe korisnika
• Potrebno je postići kompromis između jednostavnosti i
udovoljavanju zahtjevima korisnika i mogućnosti
implementacije
• Ponekad je neke zahtjeve tehnički nemoguće izvesti i treba se
pronaći zajedničko rješenje
Definiranje interface-a
• Nakon što su se sakupili svi potrebni zahtjevi kreće se u
definiranje interfejsa
• Prilikom definiranja interfejsa razmišlja se o načinu kako se
objekt koristi, a ne o načinu njegove implementacije
• U slučaju da se otkriju novi zahtjevi oni se dodaju narednim
iteracijama
• Analogija interfejsa sa primjerom taksija:
– pozovi taksi
– uđi
– specificiraj odredište
– plati vožnju …
Definiranje interface-a
Taksi
+pozoviTaksi:void
+uđiUtaksi:void
+specificirajOdredište:void
+platiVožnju:void
+izađi:void
Identifikacija implementacije
• Tek nakon što su interfejsi odabrani kreće se sa identifikacijom
implementacije
• Tehnički gledano – sve što nije dio interfejsa se smatra
implementacijom
• Sve privatne metode se smatraju implementacijom
• Korisnik bi trebao vidjeti samo način pozivanja/komunikacije s
interfejsom, ali ne i sam kod koji te metode sadrže
• Sve što se smatra implementacijom bi dizajner klase trebao
moći promijeniti (u slučaju da je to potrebno) bez da se to
odrazi na krajnjeg korisnika
Zadatak
Sequence
-numbers: int[]
-length : int
+generateSequence(length):void
+printNumber: void
+checkUserInput: bool
Sequence
class Sequence {
private:
int numbers[100];
int length = 0;
public:
...
};
+generateSequence(length):void
void generateSequence(int receivedLength) {
length = receivedLength;
for (int i = 1; i <= length; i++) {
numbers[i] = rand() % 10;
}
}
+printNumber:void
void printNumber() {
cout << "Duljina " << length << ", broj je:" << endl;
for (int i = 1; i <= length; i++) {
cout << numbers[i];
}
Sleep(2000);
system("CLS");
}
+checkUserInput:bool
bool checkUserInput() {
cout << "Duljina " << length << ", broj je:" << endl;
for (int i = 1; i <= length; i++) {
char c = _getche();
int broj = c - '0’;
if (broj != numbers[i]) return false;
}
return true;
}
ČitačBazePodataka
ČitačBazePodataka
-imeBaze:string
-pozicijaPokazivaca:int
+ČitačBazePodataka(string)
+ČitačBazePodataka(string, int)
+otvori:void
+zatvori:void
+idiNaPrviElement:void
+idiNaZadnjiElement:void
+dohvatiBrojElemenata:int
+pročitajZapis:String
Potpis
public string dohvatiZapis(int redniBroj)
Konstruktori
class CitacBazePodataka{
string imeBaze;
int pozicijaPokazivaca;
int main(){
Derived d2(10);
return 0;
}
int main() {
Liger liger;
return 0;
}
C++
• Base konstruktor se poziva automatski
• C++ podržava višestruko nasljeđivanje
• Možemo pozvati i parametrizirani konstruktor Base
klase, ali to moramo posebno naglasiti
class Parent(object):
def __init__(self):
print ("Calling parent constructor")
class Child(Parent):
def __init__(self):
print ("Calling child constructor")
c = Child()
class Parent(object):
def __init__(self):
print ("Calling parent constructor")
class Child(Parent):
def __init__(self):
super(Child, self).__init__()
print ("Calling child constructor")
c = Child()
class Lion(Animal):
def __init__(self):
super(Lion, self).__init__()
print ("Calling Lion constructor")
class Tiger(Animal):
def __init__(self):
super(Tiger, self).__init__()
print ("Calling Tiger constructor")
liger = Liger()
class Lion(Animal):
def __init__(self):
super(Lion, self).__init__()
print ("Calling Lion constructor")
class Tiger(Animal):
def __init__(self):
super(Tiger, self).__init__()
print ("Calling Tiger constructor")
liger = Liger()
Python
• Možemo definirati samo jedan konstruktor
• Python podržava višestruko nasljeđivanje
• Redoslijed je bitan
• Ručno moramo pozivati konstruktor base klase korištenjem „super”
• Postoje različiti MRO
https://python-history.blogspot.com/2010/06/method-resolution-order.html
class Program{
static void Main(string[] args){
Derived d = new Derived();
}
}
class Program{
static void Main(string[] args){
Derived d = new Derived(100);
}
}
class Program{
static void Main(string[] args){
Derived d = new Derived(100);
}
}
public Base(){
System.out.println("Base default constructor!");
}
public Base(int x){
System.out.println("Base parameterized constructor!");
}
}
KRAJ