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 vrijednost public 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 public string dohvatizapis(int rednibroj) Potpis = dohvatizapis (int rednibroj) ime metode + lista argumenata Potpis uključuje ime metode i listu 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
Konstruktori class CitacBazePodataka{ string imebaze; int pozicijakursora; //inicijalizacija samo imena public CitacBazePodataka(string ime){ imebaze = ime; pozicijakursora = 0; //inicijalizacija imena i kursora public 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 kod brojac = 0; brojac = brojac / 5; catch (ArithmeticException e){ //Kod za hvatanje iznimke Ispis(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 Metoda1(){ int brojac; this.brojac = 1; public class Broj{ int brojac; public Metoda1(){ int brojac; ; public Metoda2(){ int brojac;
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 Matrica a, b, c; c = a + b; 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
Preopterećivanje operatora Complex a(1.2, 1.3); complex numbers //this class is used to represent 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 operatora class 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-level implementacijom
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? Apstrakcija na razini Zaposlenik Apstrakcija na razini liste class Zaposlenik : public ListContainer{ public:...//javne metode void DodajZaposlenika(Zaposlenik zaposlenik); void UkloniZaposlenika(Zaposlenik zaposlenik); Zaposlenik SljedećiElementListe(); Zaposlenik PrviElement(); Zaposlenik ZadnjiElement();... private: Miješanje apstrakcija Apstrakcija na razini zaposlenik Apstrakcija na razini Lista Klasa nasljeđuje ListContainer ;...
Interfejs Apstrakcija svih metoda na razini Zaposlenik Klasa koristi ListContainer biblioteku koja je sada private class Zaposlenik{ public:...//javne metode void DodajZaposlenika(Zaposlenik zaposlenik); void UkloniZaposlenika(Zaposlenik zaposlenik); Zaposlenik SljedećiZaposlenik(); Zaposlenik PrviZaposlenik(); Zaposlenik ZadnjiZaposlenik();... private: ListContainer listazaposlenika;... 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 samo za is-a odnose? ;