Objektno programiranje

Uvod

Objektno-orijentirano programiranje je programska paradigma bazirana na konceptu objekata koji mogu sadržavati podatke u obliku polja, znanih kao atributi i procedura, znanih kao metode. Procedure mogu pristupiti i modificirati objekte. Programi pisani OOP-em dizajnirani su tako da su objekti u stalnoj interakciji jedni s drugima. Ima značajnih razlika u objektno-orijentiranim programskim jezicima, ali najpopularniji su bazirani na klasama, što znači da su objekti neki oblik klasa koje određuju njihov tip.

Neki od najpopularnijih programskih jezika su višeparadigmatski programski jezici koji podržavaju OOP većeg ili manjeg stupnja. Najpopularniji takvi programski jezici su C++, Java, Python, C#, PHP, Ruby, Perl, Objektni C. Neki jezici poput Perl-a su sve manje u upotrebi jer su zamijenjeni novijim, boljim objektno-orijentiranim programskim jezicima.

Programski jezici

Objekt

Pojam objekta svima nam je poznat – konkretna stvar koju možemo osjetiti, dodirnuti i rukovati njome. Još kao mala djeca naučimo čemu koji objekt služi, koja mu je namjena i kako se koristiti njime. Definicija objekta u kontekstu razvoja softvera zapravo je vrlo slična. Softverski objekti naravno nisu materijalne stvari koje se mogu dotaknuti, osjetiti ili podići, no oni jesu modeli nečega što može napraviti određene stvari i nečega čemu se nešto može napraviti. Službeno, objekt je skup podataka i njima pripisanih ponašanja.

Nakon što smo se upoznali s pojmom objekta, zanima nas što znači pojam „objektno orijentirano“. Navedeni pojam predstavlja nešto što je funkcijski usmjereno prema modeliranju objekata. Ovo je jedna od mnogih tehnika korištenih za modeliranje kompleksnih sustava putem opisivanja skupa objekata u međusobnoj interakciji. Pojmovi usko povezani uz pojam objektno-orijentiranog su objektno orijentirana analiza, objektno orijentiran dizajn i objektno orijentirano programiranje. Analiza, dizajn i programiranje su zapravo koraci pri bilo kojoj vrsti razvoja softvera.

Objektno orijentirana analiza (OOA) je proces proučavanja problema, sustava ili nekog zadatka i identificiranja objekata koji se pojavljuju u tom problemu i interakcija između tih objekata. U procesu analize se zapravo određuje što se treba napraviti te je rezultat ovog procesa lista zahtjeva. U razvoju softvera, analiza se najčešće sastoji od intervjuiranja korisnika, proučavanja aktivnih procesa i eliminacije mogućnosti.

Objektno orijentiran dizajn (OOD) je proces pretvaranja zahtjeva iz prethodnog koraka u specifikaciju implementacije. Programer mora imenovati objekte, definirati metode i funkcije i formalno specificirati koji objekti mogu aktivirati koje funkcije. Svrha ove faze je dobiti kompletnu implementaciju koja zadovoljava sve potrebe zadatka.

Objektno orijentirano programiranje (OOP) je završni proces u kojem se jasno definirani dizajn pretvara u ispravan i funkcionalan program koji izvršava upravo ono za što je i napravljen.

Nerealno je očekivati da će se svaki razvoj programa odvijati idealno u ove tri faze. Koliko god mi pokušavali odvojiti ove tri faze, uvijek će isplivati stvari koje će se tijekom dizajna trebati dodatno analizirati ili stvari koje će se tijekom programiranja trebati dodatno definirati u dizajnu. Većina suvremenog softvera razvija se na način da se prvo modelira, dizajnira i programira mali dio zadatka i onda se on postepeno širi kako bi se poboljšali određeni dijelovi i dodali novi.

Klase

U prethodnom smo poglavlju definirali što je to objekt. No kako razlikovati objekte od klasa? Na primjer, i kruške i jabuke su objekti, no jasno je da to nije ista stvar. Zamislimo sada da radimo aplikaciju za farmu za uzgoj voća. Pretpostavimo da se jabuke skladište u bačve, a kruške u košare. Na taj način dobivamo četiri vrste objekta: jabuke, kruške, bačve i košare. U objektnom programiranju umjesto termina „vrsta objekta“ koristi se termin „klasa“ tako da po službenoj terminologiji dobili smo četiri klase objekata. Razlika između objekta i klase je ta što klase zapravo opisuju objekte. Uzmimo za primjer da na stolu imamo tri naranče. Svaka od tih naranča je zaseban objekt, no sve tri naranče pripadaju istoj klasi. Prema tome, na stolu imamo tri objekta jedne klase. Veze između klasa prikazuju se klasnim dijagramima, tzv. UML dijagramima.

Slika 1
Slika 1.

Slika 1 jasno prikazuje da je jabuka na neki način povezana sa bačvom i kruška sa košarom, no vrlo često će se dijagram koristiti kako bi se iz njega iščitalo mnogo više informacija. Malo opširniji i detaljniji dijagram prikazan je na Slici 2.

Slika 2
Slika 2.

Osim što povezuje određene klase, on strelicom i tekstom prikazuje da jabuke idu u bačve, dakle imenuje relaciju između ove dvije klase i pokazuje što ide u što. Ovaj dijagram nas također informira i o mogućoj količini nekog objekta u određenoj relaciji. Jedna košara može sadržavati više, konačno mnogo krušaka (na dijagramu prikazano znakom „ * „), dok kruške mogu ići samo u jednu košaru. Navedeni broj se naziva mnogobrojnost objekta.

Podaci i ponašanja (Data and behaviors)

Podatak objekta predstavlja individualne karakteristike određenog objekta. Klasa navodi specifičan skup karakteristika koje su zajedničke svim objektima te klase. Svaki objekt može imati različite podatke za neku karakteristiku. Na primjer, svaka od tri naranče na stolu može imati različitu masu. Tada bi klasa naranče imala masu kao jedan od atributa. Sve naranče imaju atribut mase, ali se vrijednost atributa mase razlikuje od naranče do naranče. Vrijednost atributa, naravno, ne mora biti jedinstvena. Moguće je i prihvatljivo da dvije naranče, odnosno dva objekta imaju istu masu. Atribute se često naziva članovima, odnosno svojstvima ovisno o literaturi. Vratimo se na primjer farme voća. Farmeru bi svakako bilo korisno znati neke informacije i karakteristike objekata. Na primjer, zanima ga masa i datum berbe kruške, boja jabuke, lokacija košare, veličina bačve i tako dalje. Na slici 3 vidimo primjer detaljnijeg dijagrama koji osim svojstva objekata, za svako svojstvo navodi i tip podatka.

Slika 3
Slika 3.

Atributi su najčešće osnovni tipovi podatka koji se pojavljuju u većini programskih jezika. Cijeli broj (integer), realni broj (floating-point number), niz znakova (string) su najosnovniji primjeri tipova podataka. Tipovi atributa mogu također biti predstavljeni i kao strukture podataka (liste, stabla, grafovi…).

Ponašanja (behaviors) su radnje koje se izvršavaju nad objektima. Ponašanja (behaviors) koja se mogu izvršiti na određenoj klasi objekta zovu se metode. Baš kao funkcije u matematici, metode primaju ulazne parametre i vraćaju vrijednosti. Parametri, u slučaju metoda, su objekti nad kojima onda ta metoda izvršava željene radnje i vraća rezultate te radnje. Moguća akcija na primjeru farme voća je berba krušaka. Akcija ubiranja funkcionirala bi tako da bi dodala jednu krušku na listu krušaka pod klasom košara. Mnogo je mogućih akcija za ovaj konkretan primjer: prodaja krušaka iz košare, odbacivanje trulih jabuka i tako dalje.

Primjer

Povežimo novonaučeno znanje o objektno-orijentiranom programiranju tako što ćemo proći kroz nekoliko iteracija objektno-orijentiranog dizajna na primjeru iz stvarnog svijeta. Za primjer uzet ćemo knjižnicu. Moderne knjižnice prate svoj inventar preko web sučelja. Na taj način svaki član knjižnice može pratiti dostupnost određene knjige od kuće i ako je knjiga slobodna može ju rezervirati kako bi bio siguran da tu knjigu neće uzeti netko prije njega.

Počet ćemo s analizom. Knjižničar ima zadatak napraviti novi katalog knjiga u novom programu jer je dosadašnji star i neugodan za korištenje te ne daje dovoljno podataka o knjigama. Katalog sadrži popis knjiga. Preko kataloga se pretražuju knjige po temi, naslovu ili autoru. Svaka knjiga je označena preko ISBN-a, međunarodnog standardnog knjižnog broja (International Standard Book Number). Svaka knjiga ima ima i svoj DDS, broj dodijeljen prema Deweyevoj decimalnoj klasifikaciji (Dewey Decimal System) kako bi se knjiga pronašla na kojoj je polici. Prema ovoj analizi, knjiga nam je najvažniji objekt s nekoliko svojih atributa. Isto tako želimo spremiti nekoliko podataka o autoru pa je autor isto objekt. Nadalje neke knjige imaju više autora pa svakoj knjizi treba dodijeliti listu autora. Veza između autora i knjige je udruženje (association). Prikazat ćemo sve UML dijagramom.

Slika 8
Slika 8.

Međutim, u knjižnici ne postoje samo knjige. Tu su časopisi, video i audio zapisi. Nijedno od toga nema ISBN broj. Časopisi imaju imena, brojeve, godina izdavanja, video zapisi su uglavnom na DVD-u i imaju svoj naslov, glumce, redatelja i žanr. Audio zapisi su na CD-u i njih možemo smatrati audio knjigama s autorom i naslovom. Nastao je problem jer više ne možemo koristiti samo objekt knjiga. Treba nam naslijeđivanje (inheritence). Klasificirat ćemo sve na objekt stvar. Pod stvari podrazumijevamo objekte knjiga, časopis, DVD i CD zapis.

Slika 9
Slika 9.

Prilikom pretraživanja određene riječi, korisnik pošalje zahtjev katalogu i on prikaže sve stvari koje je pronašao, bez obzira da li se radi o knjizi ili ne, sve stvari su iste u ovom slučaju. Pozivamo se na lociranje (locate()), metodu koja nam govori što ta stvar zapravo jest, koji objekt i govori nam gdje se nalazi u knjižnici. Prikazujemo UML dijagram sekvenci koji će nam, za razliku od dijagrama klasa koji prikazuje odnose među objektima, pokazati koje se poruke razmjenjuju između pojedinih objekata. Pune crte pokazuju na koju smo se metodu pozvali, a isprekidane crte prikazuju nam traženi atribut. Na navedenom dijagramu tražimo sve objekte koji sadrže riječ “zečići” (eng. bunnies).

Slika 10
Slika 10.

S obzirom da nam se u svakom objektu pojavljuje autor, a on može biti glumac, redatelj, pisac itd. treba nam klasa osoba. Za tu klasu nam je dovoljno znati kako je on doprinio tom djelu pa će nam dovoljno biti objekt doprinositelj. Prebacit ćemo atribut koji nam prikazuje osobu na njegovog roditelja na UML dijagramu, tj. na doprinositelja.

Slika 11
Slika 11.

Relacija između doprinositelja i stvari je mnogi-na-mnoge (eng. many-to-many relationship). Kako jedna stvar može imati više doprinositelja, bolja opcija je jednostavno dodati atribute na svaku podklasu, autora za knjige, glumca i redatelja za DVD, itd. Zatim svaku podklasu upariti s doprinositeljem. Time izgubimo dosta polimorfne elegancije, koju ćemo vratiti tako da na objekt stvar dodamo metodu uzmi_doprinositelja. Na taj način katalog ne mora znati koji atribut objekt traži. UML dijagram sada izgleda ovako:

Slika 12
Slika 12.

Ovaj dijagram nam ne djeluje stabilno, otežava nam održavanje i proširivanje kataloga. Ima previše relacija i previše klasa na koja bi utjecala i mala modifikacija na bilo kojoj klasi. Odgovara nam nasljeđivanje. Vratimo se na prijašnji dijagram koji je imao doprinositelje spojene direktno na stvari. Dodat ćemo novu klasu koja će identificirati tip doprinositelja. Ovo je važan korak u objektno-orijentiranom dizajnu. Uvodimo klasu u dizajn koja će opisivati druge objekte, umjesto da modificiramo neki od inicijalnih zahtjeva. Rekonstruiramo dizajn tako da odgovara objektima u sustavu, radije nego da rekonstruiramo objekte u stvarnom životu. Rekonstrukcija je nužan proces u održavanju programa i dizajna. Cilj rekonstrukcije je brisanje duplikata i kompliciranih relacija kako bi dobili jednostavniji i elegantniji dizajn. Naš dijagram sad ima dodan tip doprinositelja, a na stvar ćemo dodati atribut doprinostelja.

Slika 13
Slika 13.

Dizajn temeljen na naslijeđivanju ima prednost jer nam omogućuje dodavanje novih doprinositelja bez dodavanja novih klasa u dizajn. Naslijeđivanje je najkorisnije kad podklase trebaju specijalizaciju. Specijalizacija je stvaranje ili mijenjanje atributa ili ponašanja na podklasi čineći je različitom od klase roditelja. Nema smisla stvoriti puno praznih klasa samo da bi identificirali različite objekte. Ako pogledamo verziju dijagrama samo za doprinositelje imat ćemo puno podklasa koje ustvari neće raditi ništa.

Slika 14
Slika 14.

Neki put je važno prepoznati kad ne trebamo koristiti objektno-orijentirani princip. Ovaj primjer nam služi kako bi vidli da su objekti samo alati, a ne nužno i pravila.

Sakrivanje detalja i stvaranje javnog sučelja

Ključna svrha oblikovanja objekta u objektno-orijentiranom dizajnu je odrediti što će javno sučelje tog objekta biti. Sučelje je skup atributa i metoda koje drugi objekti mogu koristiti da bi međusobno komunicirali s tim objektom. Drugi objekti ne trebaju, a i često im nije dopušteno, pristupiti unutarnjem djelovanju objekta. Uobičajen primjer iz „stvarnog života“ je televizija. Naše sučelje za televizor je daljinski upravljač. Svaki gumb na daljinskom upravljaču predstavlja metodu koja može biti korištena na televizoru (objektu). Kada mi pristupimo tim metodama, mi ne znamo i ne zanima nas dobiva li televizor signal od antene, satelitskog tanjura ili nečeg trećeg. Nije nas briga kakvi se elektronski signali šalju da prilagode jačinu zvuka ili je li zvuk namijenjen slušalicama ili zvučnicima. Kada bi otvorili televizor da možemo pristupiti unutarnjem djelovanju, npr. da odvojimo „output“ signal na oba dva vanjska zvučnika i slušalice, poništili bi garanciju.

Ovaj proces sakrivanja implementacije ili detalja funkcioniranja objekta je nazvan „sakrivanje informacija“ („information hiding“). Također se ponekad pojavljuje kao „enkapsulacija“ („encapsulation“). Razlika između enkapsulacije i sakrivanja informacija je u velikoj mjeri nebitna, posebno na razini dizajna.

Zapamtimo, objekti programa mogu predstavljati prave objekte, ali to ih ne čini pravim objektima. Oni su modeli. Jedna od najvećih prednosti modeliranja je mogućnost ignoriranja nebitnih detalja. Modeli auta koji djeca grade dok su mali mogu izgledati kao pravi auti, međutim taj auto je nemoguće pokrenuti. Takvi detalji su bili prekomplicirani i nebitni prije nego što djeca počnu voziti. Takav model je „apstrakcija“ („abstraction“) realnog koncepta.

„Apstrakcija“ je još jedan objektno-orijentirani koncept vezan uz enkapsulaciju i skrivanje informacija. Jednostavno sročeno, apstrakcija označava sakrivanje unutrašnjih detalja i pokazivanje funkcionalnosti. Vozač auta treba biti u interakciji s volanom, gasom i kočnicama. Djelovanja motora, podsistema kočnica i takve stvari nisu bitne vozaču. S druge strane, mehaničar radi na različitim razinama apstrakcije, npr. ugađanje motora i podešavanje kočnica.

Slika 4
Slika 4.
Kada dizajniramo sučelje, pokušavamo se staviti u cipele tog objekta i zamisliti da objekt ima veliku potrebu za privatnošću. Ne smijemo dati drugim objektima da imaju pristup podacima o nama (objektu) osim ako se nama ne čini da je to u najboljem interesu da imaju pristup. Ne smijemo im dati sučelje da nas forsiraju da napravimo određen zadatak osim u slučaju kada smo mi sigurni da im možemo dati tu mogućnost.

Kompozicija

Dosad, naučili smo dizajnirati sustave kao grupu objekata u međusobnom djelovanju, gdje svaka interakcija uključuje gledanje objekata na prigodnoj razini apstrakcije. Ne znamo još kako kreirati te razine apstrakcije. Postoji raznolik broj načina kako to napraviti, ali većina obrazaca dizajna se oslanjaju na 2 osnovna objektno-orijentirana principa poznata kao „kompozicija“ (composition) i „nasljeđe“ (inheritance). Kompozicija je jednostavnija, pa počnimo s tim.

Kompozicija je čin skupljanja nekoliko objekata zajedno kako bi kreirali novi. Kompozicija je obično dobar izbor kada je jedan objekt dio nekog drugog objekta. Već smo vidjeli prvi hint o kompoziciji u primjeru s autom. Auto se sastoji od motora, prijenosa, elektropokretača, svjetala i vjetrobrana među svim ostalim brojnim dijelovima. Motor se sastoji od klipova, radilice i ventila. U ovom primjeru, kompozicija je dobar način pružanja razina apstrakcije. Objekt auto može pružiti sučelje koje treba vozaču, dok u isto vrijeme pruža pristup svojim dijelovima, što nudi dublje razine apstrakcije pogodne mehaničarima. Ti dijelovi naravno mogu biti dalje secirani ako mehaničar treba više informacija kako bi našao problem ili podesio motor.

Objekti u objektno-orijentiranim sustavima ponekad predstavljaju fizičke objekte kao što su ljudi, knjige ili telefoni. Međutim, oni češće predstavljaju apstraktne ideje. Ljudi imaju imena, knjige imaju naslove i telefoni se koristi za uspostavljanje poziva. Pozivi, naslovi, računi, imena, sastanci i plaćanja nisu smatrani objektima u fizičkom svijetu, ali oni svi su često modelirane komponente računalnog sustava.

Pokušajmo modelirati više računalno orijentirani primjer da vidimo kako djeluje kompozicija. Gledat ćemo dizajne računalne igre šaha. Ovo je bila popularna razonoda akademicima u 80-ima i 90-ima. Ljudi su predviđali da će računalo jednog dana biti sposobno pobijediti ljudskog velemajstora. Kada se to i dogodilo 1997. (IMB-ov Deep Blue je pobijedio Gary Kasparova), oslabio je interes za taj problem, iako je još postojalo natjecanje između računala i ljudi. (Računala uglavnom pobjede). Šah je igra s 2 igrača, koja koristi set koji se sastoji od ploče sa 64 mjesta (8x8) i 32 figure. Igrači uzimaju red jedan za drugim gdje miču jednu od svojih 16 figura po ploči. Svaka figura može uzeti protivničke figure. Počnimo sa najvišom razinom apstrakcije koja je moguća. Imamo 2 igrača koji su u interakciji sa šahovskim setom uzimajući svaki svoj red radeći poteze. Ovo baš i ne izgleda kao dijagram klasa. To je zato jer ni nije dijagram klasa, ovo je dijagram objekata (object diagram). Opisuje sustav u određenom trenutku te opisuje određene objekte u određenim trenucima, ali ne i interakciju između klasa. Zapamtimo, oba dva igrača su članovi iste klase, tako da dijagram izgleda malo drugačije.

Slika 5
Slika 5.

Dijagram pokazuje da točno dva igrača mogu biti u interakciji sa jednim šahovskim setom. Također pokazuje da jedan igrač može igrati na samo jednom setu istovremeno.

Međutim, mi raspravljamo o kompoziciji ne o UML-u, zato razmislimo od čega se sastoji šahovski set. Nas trenutno ne zanima od čega se sastoje igrači. Šahovski set sastoji se od ploče i 32 figure. Nadalje se ploča sastoji od 64 mjesta. Mogli bi dati argument da figure nisu dio seta jer možemo zamijeniti figure u setu s drugim setom figura. Dok je ovo malo vjerojatno ili nemoguće u računalnoj verziji šaha, to nas uvodi u supsumpciju („aggregation“).

Supsumpcija je gotovo isto kao kompozicija. Razlika je u tome što supsumpcija objekata može postojati samostalno. Bilo bi nemoguće za poziciju da bude povezana za različitim pločama, zato kažemo da se ploča sastoji od pozicija. Ali za figure, koje mogu postojati neovisno o setu, kažemo da su u agregatnom odnosu s tim setom.

Drugi način razlikovanja supsumpcije i kompozicije je da razmislimo o životnom vijeku tog objekta. Ako komponirani (vanjski) objekt kontrolira kada su unutarnji objekti kreirani i uništeni, onda je kompozicija najprikladnija. Ako je unutarnji objekt kreiran neovisno o vanjskom objektu ili može nadživjeti vanjski objekt, agregirana veza ima više smisla. Također, pamtite da je kompozicija supsumpcija; supsumpcija je jednostavno generalizirani oblik kompozicije. Svaka kompozicija je supsumpcija, ali ne i obratno. Opišimo naš trenutni set kompozicijom i dodajmo par atributa objektu da drži kompozitne veze:

Slika 6
Slika 6.

Nasljeđivanje

Raspravili smo tri tipa veza između objekata: asocijacija, kompozicija i supsumpcija. Međutim, nismo još potpuno specificirali naš šahovski set i ovi alati se ne čine adekvatnim za taj posao. Raspravili smo mogućnost da igrač može biti čovjek ili da može biti dio softvera koji sadrži umjetnu inteligenciju. Ne čini se ispravno reći da je igrač povezan s čovjekom ili da je umjetna inteligencija dio objekta igrač. Što zapravo trebamo je reći da je Deepblue igrač i da je Gary Kasparov igrač.

„Je“ veza je formirana preko nasljeđivanja. Nasljeđivanje je kao obiteljsko stablo. Prezime mog djeda je Martinić i moj otac je naslijedio to prezime. Ja sam ga naslijedio od njega. U OOP, umjesto nasljeđivanja značajki i ponašanja od osobe, jedna klasa može naslijediti atribute i metode od druge klase.

Na primjer, postoje 32 figure u našem setu, ali postoje samo 6 različitih tipova figura (pijuni, lovci, topovi, konji, kraljica i kralj), od kojih svaki ima drugačije obrasce kretanja. Sve ove klase figura imaju svojstva, kao što je boja i dio seta kojem pripadaju, ali oni također imaju jedinstvene oblike i različite kretnje. Idemo vidjeti kako tih 6 tipova figura mogu naslijediti od klase Figura.

Slika 7
Slika 7.

Šuplje strelice naznačuju da individualne klase figura nasljeđuju od klase Figura. Svi podtipovi automatski imaju atribute chess_set i color koji su naslijeđeni od osnovne klase. Sada znamo da sve podklase klase Figura moraju imati metodu kretnja; inače, kada ploča pokušava pomaknuti figure, ostat će zbunjena.

Polimorfizam je mogućnost da tretiramo klasu drugačije ovisno na kojoj podklasi je implementirana.

Višestruko nasljeđivanje

Kada zamislimo nasljeđivanje našeg vlastitog obiteljskog stabla, možemo vidjeti da nasljeđujemo značajke od oba roditelja, ne samo od jednog.

Objektno-orijentirani dizajn također može pružati takvo višestruko nasljeđivanje, što dopušta podklasi da naslijedi funkcionalnost od više klasa. U praksi, višestruko nasljeđivanje može biti dosta zeznuto i neki programski jezici (najpoznatija, Java) zabranjuju višestruko nasljeđivanje. Međutim, višestruko nasljeđivanje ima svojih mogućnosti. Najčešće, može biti korišteno za kreiranje objekata koji imaju 2 jasna oblika ponašanja.

Literatura

Seminar 4 - Odabrane teme iz računarstva

Sveučilište u Zagrebu
Prirodoslovno-matematički fakultet
Matematički odsjek
Vitomir Levanić, Mislav Martinić i Hrvoje Štefek

Zagreb, travanj 2018.

Prezentacija