Upload
others
View
18
Download
1
Embed Size (px)
Citation preview
Objektno orjentirano programiranje
Predavanje 4
Objektno orijentirani koncepti
Konstruktori
• Metoda koja se poziva prilikom stvaranja novog objekta
• Svojstveni su OO programiranju
• Imaju isto ime kao i klasa i nemaju povratnu vrijednost
• Konstruktor za klasu Osoba bi izgledao ovako:
public Osoba (){
/*Kod potreban za konstrukciju */
}
Konstruktori
• Kompajler prepozna da je ime identično imenu klase i smatra tu metodu konstruktorom
• Ovo nije konstruktor:
public int Osoba (){
/*Kod potreban za konstrukciju */
}
Sintaksa neće prouzrokovati greške pri kompajliranju ali se program neće ponašati kako očekujemo
Kada se konstruktor poziva?
• Kada se stvara novi objekt, jedna od prvih stvari biti će poziv konstruktora
• Poziv se događa automatski
• Osoba x = new Osoba();
• Ključna riječ new stvara novu instancu klase Osoba i alocira potrebnu memoriju
• Konstruktor služi da bi se obavila potrebna inicijalizacija objekta
• Objekt se postavlja na svoje početno, stabilno stanje
• Npr. ako imamo atribut klase koji se predstavlja brojač:• brojač=0;
Default konstruktor
• Ako napišemo klasu koja ne sadrži konstruktor program će se ipak kompajlirati i objekt možemo koristiti
• Klasi se dodjeljuje default konstruktor
• Uvijek postoji barem jedan konstruktor
• U slučaju da se radi o naslijeđenoj klasi default konstruktor poziva konstruktor roditeljske klase
• Dobra praksa je uvijek napisati konstruktor – makar prazan, pa u njega naknadno dodati kod kada zatreba
Višestruki konstruktori
• Objekte možemo stvoriti na više načina
• Možemo napisati više različitih kostruktora
public class Count{
int count;
public Count(){
count = 0;
}
};
• Ovo je slučaj kada želimo brojač inicijalizirati sa 0
Višestruki konstruktori
• Možemo dodati i konstruktor koji postavlja counter na neku zadanu vrijednostpublic Count(int number){
count = number;
}
• Sada imamo dva konstruktora (s istim imenom) koji imaju različite funkcionalnosti
• Ovo se zove preopterećivanje metoda (eng. method overloading)
• Većina OO programskih jezika dozvoljava preopterećivanje metoda
Preopterećivanje metoda
• Isto ime metode, ali različit broj ili tip argumenata koje ona prima
• Metode imaju različiti potpis
• Potpis uključuje ime metode i listu argumenata
Potpis
public string dohvatiZapis(int redniBroj)
Potpis = dohvatiZapis (int redniBroj)ime metode + lista argumenata
UML dijagrami
• Za čitač baze podataka iz prethodnog predavanja želimo objekte stvarati na dva načina:• Da prilikom stvaranja novog objekta specificiramo ime baze na koju se želimo
spojiti
• Da specificiramo ime baze i lokaciju kursora
Konstruktoriclass CitacBazePodataka{string imeBaze;int pozicijaKursora;
//inicijalizacija samo imenapublic CitacBazePodataka(string ime){imeBaze = ime;pozicijaKursora = 0;}
//inicijalizacija imena i kursorapublic CitacBazePodataka(string ime,int pozicija){imeBaze = ime;pozicijaKursora = pozicija;}
...//ostatak koda klase};
Konstruktor roditeljske klase
• Kada koristimo nasljeđivanje moramo poznavati i konstruktor roditeljske klase (njegovu funkcionalnost)
• Kada koristimo nasljeđivanje onda nasljeđujemo atribute i metode od roditelja
• Trebamo poznavati ponašanje roditeljske klase
• Kako se nasljeđuje konstruktor?
Konstruktor roditeljske klase
• 1. poziva se konstruktor roditeljske klase (ovo se događa automatski, ne moramo sami obaviti poziv)
• 2. inicijaliziraju se atributi (npr. kao pozicija kursora u prethodnom primjeru)
• 3. Ostali dio koda konstruktora se izvršava
Dobra praksa kod dizajna konstruktora
• Inicijalizacija svih atributa
• Npr. u Javi se atribut ne može koristiti ako prethodno nije inicijaliziran
• Postavljanje na stabilno stanje atributa
• Konstruktori nam osiguraju stabilno stanje objekta nakon konstrukcije
• Npr. inicijaliziranje atributa na 0 ako se radi o djelitelju predstavlja nestabilno stanje
• Potrebno je unaprijed definirati koja su stabilna stanja svih atributa
Manipulacija pogreškama (error handling)
• rijetki su slučajevi kada se klasa napiše savršeno u prvoj iteraciji
• Vrlo je vjerojatno da će doći do neke vrste pogreške
• 4 načina nošenja s pogreškama:• Ignorirati problem – nije najbolja ideja!
• Provjeriti moguće probleme i u tom slučaju izaći iz programa
• Provjeriti moguće probleme, uhvatiti pogrešku i riješiti problem
• Generirati iznimku – često najbolji način
Ignoriranje problema
• Najgori scenarij
• Iako možda utjecaj problema i ne izgleda značajno u odnosu na cjelokupnu funkcionalnost problema – dobra praksa je ispraviti ga
• Efekt problema se može dalje propagirati u programu što vodi do ozbiljnijih pogrešaka
• Osnovno pravilo je da se aplikacija nikada ne bi smjela srušiti
• Možemo i dobivati krive rezultate bez da smo svjesni problema
Provjera problema i izlazak iz programa
• Pronađemo potencijalni problem i izađemo iz programa
• Aplikacija može korisniku prikazati poruku sa pogreškom i izaći
• Bolje od ignoriranja problema ali nije najbolje rješenje
• U ovom slučaju prija izlaska možemo sačuvati datoteke i podatke
Provjera problema i pokušaj oporavka
• Bolja opcija od izlaska iz programa
• Problem se detektira u kodu i aplikacija ga pokušava ispraviti
• U primjeru možda 1 nije ispravno rješenje, možda korisnika trebamo pitati za novu vrijednost
• Nije uvijek jednostavno pronaći gdje se pogreška prvo pojavljuje
if (a == 0)a = 1;
c = b / a;
Generiranje iznimki
• Većina OO jezika omogućava generiranje iznimki
• Iznimke na pružaju način detektiranja problema i manipulaciju
• Ključne riječi try i catch try{
//Potencijalno nesiguran kod
}catch (Exception e){
//Kod za hvatanje iznimke
}
Try-catch
• U slučaju da se generiria iznimka i try bloku – onda je catch blok „uhvati”• Izvršavanje try bloka se zaustavlja
• catch blokovi se progledavaju da se ustanovi koji blok hvata tu vrstu iznimke (može biti više catch blokova za isti try blok)
• U slučaju da jednoj razini razini nema prikladnihj catch blokova iznimka se šalje na višu razinu. U slučaju da nijedan kod ne hvata tu vrstu iznimke onda se ona šalje sustavu – rezultat je nepredvidljiv
• U slučaju da postoji prikladni catch blok – njegov kod se izvršava
• Slijed programa se onda nastavlja iza try-catch bloka
Try-catch
• Različite razine pogrešaka mogu se hvatati u try-catch bloku
• Možemo hvatati sve pogreške ili samo one specifične, npr. aritmetičke pogreške
try{
//Potencijalno nesiguran kodbrojac = 0;brojac = brojac / 5;
}catch (ArithmeticException e){
//Kod za hvatanje iznimkeIspis(e.getMessage());brojac = 1;}Ispis("Uspjesno rukovanje iznimkom");
Generalni try-catch
• Hvata bilo koju vrstu pogrešaka
try{
//Potencijalno nesiguran kod
}catch (Exception e){
//Kod za hvatanje iznimke
}
Koncept dosega (eng. scope)
• Više objekata se može stvoriti iz iste klase
• Svaki od objekata ima jedinstveno stanje i identitet
• Svaki objekt se zasebno stvara i ima svoju lokaciju u memoriji
• Neki atributi ako se definiraju na prikladan način mogu biti dijeljeni među svim objektima iste klase
• Stanje objekta je predstavljeno atributima
• Postoje tri vrste atributa:• Lokalni atributi
• Atributi objekta
• Atributi klase
Lokalni atributi
• Lokalni atributi su varijable pojedinih metoda
• Atributi postoje u određenom dosegu
• Kada se Metoda1 pozove brojač se stvara i kada završi s radom uništava se
public class Broj{
public Metoda1(){int brojac;
}public Metoda2(){
}};
Atributi objekta
• Postoje situacije kada je potrebno da se neki atribut objekta zajednički koristi od strane više metoda tog objekta
public class Broj{int brojac; //Dostupan metodama Metoda1 i Metoda2
public Metoda1(){brojac = 1;
}public Metoda2(){
brojac = 2;}
};
Atributi objekta
• U ovom slučaju atribut brojač je definiran izvan dosega obje metoda, ali ga one mogu koristiti jer je unutar dosega klase
• Sve te metode koriste isti atribut u memoriji
• Međutim, atribut brojač se ne dijeli među različitim objektima
Atributi objekta - primjer
• Postoje tri zasebne memorijske lokacije za atribute brojač
• Korištenje this pointera za pristup atributu objekta
• this – je referenca na trenutni objekt
public class Broj{int brojac; public Metoda1(){
int brojac;
}public Metoda2(){
int brojac;}
};public Metoda1(){
int brojac;this.brojac = 1;
}
Atributi klase
• Moguće je da više objekata dijeli isti atribut
• U Javi, C# i C++ se ovo može napraviti deklaracijom atributa kao static
• Na ovaj način se koristi samo jedna fizička lokacija u memoriji
public class Broj{static int brojac;
public Metoda1(){
}
};
Preopterećenje operatora (eng. operator overloading)• Neki jezici poput C++ dozvoljavaju preopterećenje operatora
• Definiramo novu funkcionalnost za operator
• x=5+6;
• Većina ljudi kada vidi + operator pretpostavlja zbrajanje
• + može predstavljati i druge funkcionalnosti ovisno o tipu, npr. konkatenacija stringa
String ime = "Ivan", prezime = "Ivić";String imeIPrezime = ime + " " + prezime;
Preopterećivanje operatora
• Možemo definirati svoj operator koji npr. zbraja matrice
• Međutim – ovo može zbuniti programere ako ne znaju koji su operatori preopterećeni i što rade
• Neki OO jezici zbog toga ne dozvoljavaju peropterećenje operatora, npr. Java i .NET jezici
• Ako budete koristili preopterećivanje operatora u C++ jeziku treba voditi računa da se ne zbune ostali koji budu koristili te operatore
Matrica a, b, c;c = a + b;
Preopterećivanje operatora
Complex a(1.2, 1.3); //this class is used to represent complex numbers
Complex b(2.1, 3); //notice the construction taking 2 parameters for the real and imaginary part
Complex c = a + b; //for this to work the addition operator must be overloaded
//Bez preopterećenog + operatora poziv zbrajanja igledao bi ovako
Complex c = a.Add(b);
Preopterećivanje operatoraclass Complex
{
public:
Complex(double re, double im)
:real(re), imag(im)
{};
Complex operator+(const Complex& other);
Complex operator=(const Complex& other);
private:
double real;
double imag;
};
Complex Complex::operator+(const Complex& other){double result_real = real + other.real;double result_imaginary = imag + other.imag;return Complex(result_real, result_imaginary);}
Operacije nad objektima
• Kod kopiranja ili uspoređivanja primitivnih tipova podataka proces je vrlo jednostavan
• Problem kod kompleksnih struktura podataka ili objekata je da mogu sadržavati reference
• Kopiranje reference ne kopira strukturu podataka ili objekt koji se referencira
• Ista problem je sa usporedbama, uspoređuju se reference, a ne objekti koje se referencira
• Objekti mogu sadržavati reference na objekte koji sadrže reference (problem ima svoju dubinu)
Operacije nad objektima
• Plitko kopiranje (eng. shallow copy) –kopiraju se samo refrence
• Duboko kopiranje (eng. deep copy) –kopiraju se i objekti koji su referencirani (potrebno je proći cijelu dubinu stabla ako ima više nivoa referenciranja)
• Klase bi također trebale pružiti i funkciju za usporedbu – da smo sigurni da na ispravan način uspoređujemo objekte
OO - generalni pojmovi
• ADT – Abstract Data Types (apstraktni tipovi podataka) – generalan pojam koji označava kolekciju podataka i operacije nad tim podacima
• Bez razumijevanja ADT programeri stvaraju klase koju su to samo po imenu – u stvarnosti su samo okvir za podatke koji su slabo povezani
• S razumijevanjem ADT programeri mogu dizajnirati klase koje su u početku lakše za implementaciju i kasnije lakše za održavanje
• Korištenjem ADT možemo manipulirati entitetima iz stvarnog svijeta umjesto da baratamo low-level entitetima
• Npr. možemo dodati novu ćeliju u tablicu umjesto da dodajemo čvor u vezanu listu, možemo dodati novi vagon u simulator vlaka, ukratko, možemo razmišljati u okvirima stvarnih entiteta
Primjer – korištenje fontova
trenutniFont.PostaviVeličinu(veličina)trenutniFont.UključiBold()trenutniFont.IsključiBold()trenutniFont.UključiItalic()trenutniFont.IsključiItalic()trenutniFont.PostaviTipFonta(tip)
Prednosti korištenja ADT
• Skrivanje implementacijskih detalja
• Promjene ne utječu na cijeli program
• Informativniji interfejs (prilagođavanje imena u sučelju specifičnim operacijama)
• Jednostavnije poboljšati performanse
• Manje pogrešaka – posljedica jednostavnosti korištenja (trenutniFont.UključiBold())
• Postaje lako dokumentirati funkcionalnosti
• Ne moramo slati veliku količinu odvojenih podataka kroz program
• Možemo raditi sa entitetima iz stvarnog svijeta umjesto sa low-levelimplementacijom
Primjer ADT-a
• Napisati softver koji kontrolira sustav za hlađenje nuklearnog reaktora
• Možemo sustav za hlađenje tretirati kao ADT tako da definiramo sljedeće operacije za njega:
sustavHladjenja.DohvatiTemperaturu()sustavHladjenja.PostaviBrzinuProtoka(brzina)sustavHladjenja.OtvoriVentil(brVentila)sustavHladjenja.ZatvoriVentil(brVentila)
Dobra praksa
• Koristiti standardne low-level tipove kao ADT, a ne kao low-level• Postavlja se pitanje što taj low-level tip predstavlja?
• Što predstavlja taj stog, niz ili lista?
• Ako stog predstavlja zaposlenike neke firme - treba ga tretirati kao ADT zaposlenici a ne kao stog
• Ako lista predstavlja zapise računa – možemo je interpretirati kao ADT računi
• (operacije neće biti npr. – DodajUListu() već DodajRačun())
• Trebamo koristiti najveći nivo apstrakcije koji nam je dostupan
• Koristiti i jednostavne elemente kao ADT – npr. imamo svjetlo koje ima samo dvije operacije – upali i ugasi. Dobro je napraviti ADT radi kasnije jednostavnosti, lakše dokumentacije i lakše mogućnosti naknadne promjene
Dobra praksa
• Tretirati ADT neovisno o mediju na koji se pohranjuje• Recimo da imamo veliku tablicu za usluge osiguravateljske kuće koja je zbog
veličine uvijek pohranjena na disku
• Nije dobra praksa: OsiguranjeFile.Read()
• Ako kasnije promijenimo implementaciju pa se tablica čuva u memoriji ime ADT-a više nije aktualno
• Imena klasa bi trebala biti neovisna o mediju na kojem se koriste
Dobar interfejs klase?
• Miješanje apstrakcija
• Apstrakcija na razini zaposlenik
• Apstrakcija na razini Lista
• Klasa nasljeđuje ListContainer
class Zaposlenik : public ListContainer{public:...//Javne metodevoid DodajZaposlenika(Zaposlenik zaposlenik);void UkloniZaposlenika(Zaposlenik zaposlenik);
Zaposlenik SljedećiElementListe();Zaposlenik PrviElement();Zaposlenik ZadnjiElement();...private:
...};
Apstrakcija na razini Zaposlenik
Apstrakcija na razini liste
Interfejs
• Ova klasa ne nasljeđuje ListContainer
• Neki ovo koriste radi jednostavnosti koju pruže nasljeđivanje – npr. u ovom slučaju funkcionalnosti pretraživanja ili sortiranja liste
• Poterebno je postaviti pitanje - da li je nasljeđivanje korišteno samoza is-a odnose?
class Zaposlenik{public:...//Javne metodevoid DodajZaposlenika(Zaposlenik zaposlenik);void UkloniZaposlenika(Zaposlenik zaposlenik);Zaposlenik SljedećiZaposlenik();Zaposlenik PrviZaposlenik();Zaposlenik ZadnjiZaposlenik();...private:ListContainer listaZaposlenika;...
};
Apstrakcija svih metoda na razini Zaposlenik
Klasa koristi ListContainer biblioteku koja je sada private