nav_dugme codeBlog codeBlog
  • početna
  • Učionica
  • Saveti
  • Zanimljivosti
  • Kontakt

Uvod u objektno orijentisano programiranje

Viber
zoom_plus zoom_minus

Uvod

Objektno orijentisano programiranje je programska paradigma (pristup/metoda), koja podrazumeva rešavanje problema kreiranjem i međusobnom interakcijom objekata.

  • objekte možemo shvatiti kao celine u programskom kodu koje odlikuje određeno stanje i ponašanje
  • objekti nastaju instanciranjem klasa - programskog koda kojim se definišu moguće odlike budućih objekata

Ovakva definicija nije u stanju da mlađe programere koji se sa ovom tematikom prvi put sreću (ali pri tom već imaju bar ponešto početnog iskustva sa osnovnim, "ne-objektnim" pristupima u programiranju), uputi u sve pojedinosti i takođe, iz iskustva znamo da većina programera na početku ima poteškoća sa razumevanjem osnova objektno orijentisanog programiranja, tako da: dajte sebi vremena da 'pohvatate konce' kako dolikuje i da stvari razumete na pravi način (i naravno - od nečega se mora početi). ).

Razlike u odnosu na proceduralni pristup i malo istorije

Verujemo da većina čitalaca koji se prvi put hvataju ukoštac sa tematikom objektno orijentisanog programiranja već ima okvirnu predstavu o tome da je navedeni pristup veoma široko rasprostranjen u programiranju, a često se javlja i pitanje da li, pošto savladamo OOP tehnike (za pojam objektno orijentisanog programiranja, koristićemo u nastavku i uobičajenu skraćenicu OOP), ovakav pristup treba da koristimo uvek (a verovatno i pitanje - da li moramo), umesto "običnog"/proceduralnog?!

U tom smislu, lako može delovati 'preterano i nepotrebno', da za programe kao što je 'pretvaranje cene u evrima u cenu u dinarima (shodno unetom kursu)' koristimo "instanciranje klasa" ("šta god to bilo" - saznaćemo uskoro), koje sadrže uredno strukturirane kolekcije podataka i pripadajućih metoda - jer se navedeni problem mnogo lakše rešava običnim, proceduralnim pristupom na koji ste već navikli.

Ako tako razmišljate, recimo da ste u pravu (u povećoj meri), ali je takođe, za (iole) kompikovanije zadatke, kreiranje klasa koje odgovaraju podacima nad kojima program treba da operiše, krajnje uobičajena praksa.

U smislu toga kakvo mesto objektno orijentisano programiranje zauzima u IT industriji, možemo napraviti poređenje sa pojavom motora sa unutrašnjim sagorevanjem u auto industriji: nije u pitanju jedini pristup - ali je najuobičajeniji i najrasprostranjeniji.

Takođe, jednostavni primeri, nalik onome koji smo naveli, ni izdaleka nisu jedina vrsta programa koji se pišu bez upotrebe OOP tehnika (ima i znatno ozbiljnijih).

Dakle, OOP pristup nije nešto što moramo koristiti ("obavezno"/"uvek"), već nešto što možemo koristiti po potrebi - s tim da se takva potreba javlja vrlo često. Takođe verujemo da ćete tokom čitanja ovog članka, a pogotovo širim i dubljim proučavanjem programiranja, sami doći do zaključka šta su prave pogodnosti objektno orijentisanog pristupa, kako vam takav pristup može pomoći i gde i kako ga treba koristiti: najprostije rečeno, u programima čija kompleksnost zavređuje takav pristup, što u praksi znači (da ponovimo) - jako često, ali, svakako ne uvek.

Potreba da se podaci u programima organizuju na što bolji način primećena je vrlo rano u razvoju programskih jezika i već je (sada već dalekih) šezdesetih godina dvadesetog veka OOP pristup počeo da poprima oblik. Na potencijal ovakvih, u to vreme novih, tehnologija, prvo su "bacili oko" ljudi iz agencija za istraživanje svemira. Nije bilo teško uvideti da računari mogu pomoći u simuliranju procedura vezanih za svemirske letelice i sl, ali da je preduslov za tako nešto to da programi budu što pregledniji (naravno, računalo se tada na primenu OOP pristupa i u drugim oblastima).

I upravo to je (pomalo uprošćeno, ali, za sam početak, reklo bi se više nego adekvatno) glavna prednost OOP pristupa u odnosu na proceduralni - preglednost koda.

Sve ono što nudi objektno orijentisani pristup, može se zapisati i deklarisanjem zasebnih promenljivih i nezavisnih funkcija, naravno - uz nezanemarljivo povećanje složenosti kodnog zapisa.

Tokom vremena, OOP pristup našao je mesto u (rekli bismo) svim (iole popularnim / korišćenim) programskim jezicima: C++, Java, C#, Python, JavaScript, PHP, kao i mnogim drugim. neki od njih su izrazito usmereni na klase i objekte (na primer, Java), dok su drugi manje izričiti.

Naizgled može delovati malo "neubedljivo" kada navedemo da je jedna od nedvosmisleno najbitnijih prednosti OOP pristupa "samo" preglednost koda.

Možda vas ni primeri iz ovog članka eventualno neće ubediti u to da je preglednost koda i te kako bitna, ali verujemo da ćete do takvog zaključka doći sami - prvi put kada se susretnete sa nekim složenijim zadatkom (bilo u složenijim primerima u drugim članciima na ovom sajtu, a pogotovo sa još složenijim primerima koji vas čekaju u daljoj praksi).

Vreme je da se upoznamo sa tim kako sve izgleda u praksi, pa ćemo se za početak bavljenja objektno orijentisanim programiranjem prvo upoznati se sa najvažnijim konceptima (ili, kako neko voli da kaže, "stubovima") objektno orijentisanog programiranja.

To su:

  • Klase (apstrakcija podataka)
  • Enkapsulacija
  • Nasleđivanje
  • Polimorfizam

Svakom od ovih koncepata, posvetićemo više pažnje u zasebnim odeljcima u nastavku.

Klase i apstrakcija podataka

Klasa je programski kod (obično zapisan u zasebnoj datoteci) koji predstavlja obrazac po kome se kreiraju objekti.

Pojam apstrakcije, kao jedan od osnovnih principa u dizajnu klasa, podrazumeva odabir (samo) onih svojstava koja su bitna za izvršavanje programa i odbacivanje ostalih.

Potrebno je izabrati i opisati:

  • koji će podaci biti predstavljeni
  • kako će biti predstavljeni
  • kako će biti međusobno povezani
  • radnje koje će biti moguće obavljati nad podacima

Recimo, pojam osobe, koji je inače definisan mnogim svojstvima, kao što su telesna građa, duhovne odlike i sklonosti, odnos sa drugim osobama, svetom oko sebe (i mnogo čime još), u dizajnu klase (u smislu podataka), bio bi sveden samo na one odlike koje su za datu aplikaciju bitne.

Na primer: u programu za poslovnu administraciju, zanemarili bismo većinu navedenih svojstava i ostavili datum rođenja, adresu stanovanja i podatke o učinku na radnom mestu.

Primer još jednostavnije klase (donekle uprošćene), koja definiše geometrijska tela, možemo videti na donjoj slici:

Opšti prikaz veze između klase i objekata
Slika 1. - Opšti prikaz veze između klase i objekata: klasa definiše moguća stanja, odnosno, opšte odlike (budućih) objekata, dok objekti predstavljaju konkretne realizacije, sa konkretno izabranim svojstvima.

Klasa svakako jeste veoma jednostavna, ali, podaci su specijalizovani, odnosno izabrani prema konkretnim potrebama: opštim matematičkim svojstvima pravilnih mnogouglova, dodali smo ona svojstva koja se koriste pri prikazu na ekranu (zarad preglednosti, zanemarili smo koordinate centara).

Takođe, slika nam pruža mogućnost da uvidimo odnos između klasa i objekata: klasa je skup opštih karakteristika koje se mogu prispisati budućim objektima, dok objekat možemo shvatiti kao realizaciju klase , pri čemu je - među mogućim vrednostima različitih svojstava - izabrana određena kombinacija konkretnih vrednosti.

Pri definisanju klasa (u opštem smislu), ne postoje pravila o tome šta se "mora" uzeti u obzir.

Naravno da je često situacija veoma jasna i nedvosmislena, pogotovo kada su u pitanju izrazito jednostavne klase (na primer klasa za smeštaj podataka iz baze podataka, najčešće će imati ista polja kao i odgovarajuća tabela u bazi), ali, ako uzmemo u obzir komplikovanije primere, kao što su različite računarske simulacije (recimo, simulacija reli trka), situacija nije ni malo jednostavna: pre tridesetak godina strelica na gore je značila "auto ide napred", dok je strelica na dole značila da smo "pritisnuli kočnicu".

Današnje simulacije (zapravo, tako je već duži niz godina), vode računa o broju obrtaja motora (koliko smo jako stisnuli papučicu za gas), koliko jako smo stisnuli kočnicu, koliko smo (tačno) okrenuli volan ulevo ili udesno, da li su gume pohabane i da li je motor oštećen time što smo zaboravili da prebacimo menjač u viši stepen prenosa onda kada je motor dostigao (pre)visok broj obrtaja ....

Razvoj hardvera dozvolio je da se i softver razvije, odnosno da simulacije postanu lepše / bolje / raskošnije, ali i dalje ostaje pitanje: šta treba simulirati, a šta ne? Da li ćemo simulirati i zategnutost / otpuštenost matica, stanje opruga u sedištu vozača, uticaj temperature na širenje i skupljanje metala i slično?

U praksi, negde se mora "podvući crta". Neke stvari se ostavljaju za buduće verzije programa (kada hardver bude u stanju da sve "povuče"), a druge se (ipak) odbacuju, kao izrazito nepraktične.

U opštem smislu, elementi (članovi) klasa, su:

  • polja (i)
  • metode

Kao što smo već naveli, polja su zapisani podaci koji definišu stanje objekta, a metode su funkcije koje definišu radnje koje objekat može obavljati (nad svojim sopstvenim podacima, ili, sa drugim podacima u programu).

Polje (field) - podaci klase

Pojam polja klase u objektno orijentisanom programiranju označava pojedinačni imenovani podatak koji može biti:

  • primitivan podak koji pripadaju nekom od osnovnih tipova kao što su int, float ili char
  • objekat klase (iste ili različite)
  • kolekcija podataka (nizovi, liste, redovi, stek ...) bilo kog tipa
  • pokazivač / referenca na podatak, objekat ili kolekciju bilo kog od navedenih tipova

Svi ovi podaci zajedno, kao što smo već nagovestili ranije, opisuju stanje (budućih) objekata.

Primer klase koja definiše datum
Slika 1. - Primer klase koja definiše datum: polja klase su celobrojne vrednosti koje predstavljaju godinu, dan, mesec, sat, minut i sekund, kao i UNIX timestamp. Pored polja, pristune su i metode koje dozvoljavaju ažuriranje datuma i vode računa o usklađenosti svih podataka.

Da bi međusobne veze podataka klase bile korektne, potrebno je pravilno sprovesti postupak enkapsulacije, čemu ćemo posvetiti mnogo više pažnje u nastavku.

Metode klase

Ponašanje objekata zapisuje se preko metoda (funkcija), koje su direktno opisane (definisane) unutar klase.

U pitanju je programski kod koji je (gotovo) istovetan pojmu funkcije u programskom jeziku C (s tim da se, kao što je već navedeno, celokupan programski kod metoda opisuje unutar tela klase).

Opšti prikaz načina funkcionasanja metoda unutar klase / objekta
Slika 2. - Opšti prikaz načina funkcionasanja metoda unutar klase / objekta.

Metode mogu pristupati podacima klase, ali takođe i spoljnim podacima iz drugih delova programa.

Da bismo mogli da za prave razumemo kako se odvija interakcija između polja i objekata i pre svega - kako klasa (samim tim - i budući objekti) dobija željene odlike, počećemo polako da definišemo jednostavnu klase i proučavamo šta se sve dešava u različitim fazama razvoja klase ....

Primer jednostavne klase - osnovni oblik

Rekli smo na početku da se OOP pristup koristi onda kada su u pitanju kompleksni podaci koji takav pristup zavređuju. Naš primer će biti mnogo jednostavniji: definisaćemo klasu koja opisuje pravougaonik (primer deluje jednostavno - i jeste jednostavan - ali, upravo tako i treba da bude na samom početku), međutim, primer je sasvim adekvatan i u praktičnom smislu (u programima i skriptama u kojima se, na bilo koji način, koriste pravougaonici, više je nego očekivano da za kreiranje pravouganika bude definisana odgovarajuća klasa).

Klasa pravougaonik sadržaće polja a, b, O i P (podatake o stranicama pravougaonika, obimu i površini), metode kojima se obim i površina ažuriraju pri promeni vrednosti stranica a i b, kao i pomoćnu metodu za ispis podataka o pravougaoniku.

Uz sve navedeno, potrebno je udesiti i da ova četiri parametra uvek budu u međusobnoj vezi koja odgovara pravilima geometrije (a, da li će se to dešavati samo od sebe - videćemo u nastavku) ....

Definisaćemo prvo najosnovnije okvire klase i dodati samo navedene podatke, pa će klasa imati sledeći oblik:

C#
C++
Java
Python
	                            	
public class Pravougaonik
{
	public Double a, b, O, P;
}
									
								
	                            	
class Pravougaonik
{
	public:

	double a, b, O, P;
};
									
								
	                            	
public class Pravougaonik {
	public double a, b, O, P;
}
									
								
	                            	
class Pravougaonik:
	# ne moramo definisati polja
	# jeste čudno u odnosu na ostale
	# jezike, ali, videćemo u nastavku
	# kako sve funkcioniše

									
								
Slika 3. - Klasa pravougaonik - početak - klasa za sada sadrži samo polja za upis podataka.

Četiri polja klase Pravougaonik (četiri promenljive tipa Double) beleže stranice pravougaonika (a i b), kao i obim (O) i površinu (P).

Napomena: Osnovni jezik koji ćemo koristiti za sve primere u ovom članku, biće C#, budući da upravo ovaj jezik smatramo najprimerenijim za početno upoznavanje sa principima objektno orijentisanog programiranja, ali, budući da našim čitaocima mogu biti zanimljiviji drugi jezici, spremili smo primere i u popularnim programskim jezicima, kao što su C++, Java i Python.

Da biste automatski prebacili sve primere na drugi jezik, samo kliknite na odgovarajuće dugme za izbor jezika iznad bilo kog bloka koda i automatski će biti promenjen jezik i u ostalim blokovima (primer: da biste izabrali Python za jezik koji će se koristiti za primere u celom članku, kliknite na bilo koje "Python" dugme)

Ako želite da izbegnete da se pri prvom kliku promene svi primeri, kliknite prvo na bilo koje "C#"" dugme, a zatim na željeno dugme za izbor jezika.

Iako ovakva klasa ni iz daleka nije dovršena, niti zapravo spremna za eksploataciju (tek smo počeli), u najosnovnijem (tehničkom) smislu, već je možemo koristiti za zapis podataka - naravno, uz napomenu da će u svemu biti nepredviđenih okolnosti koje i te kako zahtevaju pažnju (ali, upravo time se i bavimo: učimo kako se definišu klase i objekti).

Iskoristićemo navedenu situaciju da naučimo:

  • kako se kreiraju (instanciraju) objekti
  • zašto je neophodno pravilno uspostaviti veze međe objektima (enkapsulacija)

Kreiranje objekta (rezervisana reč new)

Pozivanjem sledećeg jednostavnog koda ....

C#
C++
Java
Python
                            		
Pravougaonik P = new Pravougaonik();
									
                            	
                            		
Pravougaonik P;
									
                            	
                            		
Pravougaonik P = new Pravougaonik();
									
                            	
                            		
P = Pravougaonik()
									
                            	
Slika 4. - Kreiranje objekta P (klase Pravougaonik).

.... kreirali smo objekat P, posle čega možemo pristupiti njegovim poljima:

C#
C++
Java
Python
	                            	
P.a = 12.55;
P.b = 10.40;
Console.WriteLine(P.a);
Console.WriteLine(P.b);
									
								
	                            	
P.a = 12.55;
P.b = 10.40;
cout << P.a;
cout << P.b;
									
								
	                            	
P.a = 12.55;
P.b = 10.40;
Szstem.out.printf("%f\n", P.a);
Szstem.out.printf("%f\n", P.b);
									
								
	                            	
P.a = 12.55
P.b = 10.40
print(str(P.a))
print(str(P.b))
									
								
Slika 5. - Pristup poljima objekta P - zadavanje vrednosti.

Polja a i b sada sadrže vrednosti koje, same po sebi, imaju smisla, ali, između njih ne postoji veza koja odgovara pravilima geometrije i verujemo da se mnogi od vas pitaju zašto je to tako? Iz iskustva znamo da mnogi polaznici pri prvom susretu sa objektno orijentisanim programiranjem imaju zamisao da OOP pristup "sam od sebe", "nekako" uspostavlja sve potrebne veze između podataka i rešava usputne probleme automatski, međutim - to nije slučaj.

Uobičajeno je da nazivi klasa počinju velikim slovom (Pravougaonik), što ostavlja mogućnost da objekat (klase) nazovemo istim imenom koje počinje malim slovom (pravougaonik) i po tom početnom malom slovu ga u kodu prepoznajemo kao objekat (i time razlikujemo od klase).

Naravno, kao što vidimo iz primera, moguće je objektima davati i druge, proizvoljne nazive.

Klasa nije automatizovan šablon koji "magijskim putem" / "tek tako" / samim tim što smo klasi dali ime koje nama nešto znači (a računaru - baš ništa), definiše sve moguće objekte koji nam mogu pasti na pamet (geometrijska tela, vozila, uređaje i slično) pri čemu važe odnosi iz spoljnjeg sveta

Klasa je samo mehanizam koji omogućava da sve podatke i metode budućih objekata stavimo "pod jednu kapu" (na jedno mesto), zarad preglednosti i bolje čitljivosti, a za uspostavljanje odnosa među podacima - moramo se pobrinuti sami.

(Naravno, ništa od navedenog nije ni izdaleka "strašno", ali, svakako moramo biti pažljivi i svesni takve situacije.)

Pomenimo usput i to da se rezervisana reč new (očigledno) ne koristi u (baš) svim jezicima koje koristimo za primere (pri čemu naravno Python nije jedini jezik koji ne koristi rezervisnu reč new), ali, opšti princip instanciranja objekata je svakako veoma sličan.

Šta se tačno dešava sa podacima koje nismo inicijalizovali sami?

Ukoliko se sami ne pobrinemo za inicijalizaciju, polja objekta čiji je tip Double (isto važi i za Int32) biće inicijalizovana na vrednost 0 (nula), niske se inicijalizuju kao prazne niske, a reference imaju vrednost null.

(U slučaju klase Pravougaonik, ukoliko inicijalizujemo stranice pravougaonika, ali ne i obim i površinu, polja O i P će imati vrednost 0 - što svakako nije realistična situacija).

Dakle, budući da smo, u primeru koji koristimo, (naknadno) inicijalizovali stranice, a obim i površinu nismo inicijalizovali na odgovarajuć način (ova polja jesu inicijalizovana, ali po podrazumevanoj metodi koja im dodeljuje vrednost 0), pravougaonik ima sledeće stanje:

	                            	
// P.a = 12.55
// P.b = 10.40
// P.O = 0
// P.P = 0
									
								
Slika 6. - Stanje objekta posle nepotpune inicijalizacije.

Ovakav objekat nema praktično značenje (suštinski, nije ništa bolji nego četiri međusobno nezavisne promenljive), pa ćemo preduzeti korake da to popravimo, uvođenjem nekoliko metoda koje će kontrolisati stanje podataka (polja).

Budući da je jedna od najbitnijih stvari vezanih za promenljive u programiranju inicijalizacija, to jest, postupak kojim se promenljivama zadaje početno stanje (u našem primeru, polja a i b dobila su vrednosti tek naknadno, a poljima O i P nedostaje početno stanje koje odgovara upisanim stranicama), inicijalizacija polja je prvo na šta ćemo obratiti pažnju u nastavku.

Jasno je da za računanje (ažuriranje) obima i površine moramo imati odgovarajuće metode i jasno je da objekat ne možemo inicijalizovati prostom naredbnom dodele (kao običnu promenljivu), već i to moramo obaviti pozivom posebne metode.

Takva specijalizovana metoda u objektno orijentisanom programiranju nosi naziv konstruktor i njena svrha je zadavanje početnog stanja objekta.

Konstruktor - metoda za definisanje početnog stanja objekta

Konstruktor je specijalizovana metoda kojom se zadaje početno stanje objekta. Polja se inicijalizuju prostim naredbama dodele (ukoliko je moguće), ili pozivanjem pomoćnih metoda za ažuriranje vrednosti polja, ukoliko vrednosti određenih polja zavise od drugih polja (kao što je slučaj sa obimom i površinom pravougaonika, koji zavise od stranica).

U pisanju, konstruktor se prepoznaje po tome što (za razliku od ostalih metoda) nema povratni tip i po tome što ima isti naziv kao i sama klasa.

Dodaćemo konstruktor klasi Pravougaonik ....

C#
C++
Java
Python
	                            	
public class Pravougaonik
{
	public Double a, b, O, P;
	
	public Pravougaonik(Double a, Double b)
	{
		this.a = a;
		this.b = b;
	}
}
									
	                            
	                            	
class Pravougaonik
{
	public:

	double a, b, O, P;
	
	Pravougaonik(double a, double b)
	{
		this.a = a;
		this.b = b;
	}
};
									
	                            
	                            	
public class Pravougaonik {
	public double a, b, O, P;
	
	public Pravougaonik(double a, double b) {
		this.a = a;
		this.b = b;
	}
}
									
	                            
	                            	
class Pravougaonik:
	a, b, O, P
	
def __init__(self, a, b):
		self.a = a;
		self.b = b;
									
	                            
Slika 7. - Konstruktor dodat klasi pravougaonik. Konstruktor ima dva parametra čije će vrednosti, pri pozivu konstruktora, biti prosleđene poljima (budućeg) objekta.

.... posle čega možemo zadati početno stanje objekta na sledeći način:

C#
C++
Java
Python
	                            	
Pravougaonik P = new Pravougaonik(12.55, 10.40);
									
	                            
	                            	
Pravougaonik P(12.55, 10.40);
									
	                            
	                            	
Pravougaonik P = new Pravougaonik(12.55, 10.40);
									
	                            
	                            	
P = new Pravougaonik(12.55, 10.40)
									
	                            
Slika 8. - Kreiranje novog objekta (P) klase pravougaonik, pozivanjem novog konstruktora koji prima dve vrednosti i prosleđuje ih poljima a i b.

Kao što smo već videli, preko rezervisane reči new, poziva se konstruktor klase (pri čemu se zadaju konkretni argumenti, koji će biti prosleđeni odgovarajućim poljima).

Kao što smo takođe već videli, rezervisana reč new ne koristi se u svim jezicima, ali ćemo ostaviti našim čitaocima koji za proučavanje primera ne koriste C#, već neki od drugih jezika, da sami uvide ovakve očigledne razlike i od sada ćemo skretati pažnju samo na suštinski bitne razlike u pristupu koji se koristi u različitim OOP jezicima.

Međutim, sve je (skoro) isto kao malopre. Polja a i b odmah dobijaju konkretne vrednosti (o rezervisanoj reči this koju primećujete u kodu, više ćemo govoriti kasnije), ali, polja O i P i dalje imaju vrednost 0 (ovo smo naravno uradili isključivo iz razloga da još jednom podcrtamo da se stvari u objektno orijentisanom programiranju ne dešavaju automatski).

Uvođenjem dve nove metode: jedne za računanje obima i druge, za računanje površine, koje ćemo potom pozvati preko konstruktora (ako se neko pita, ovo je moguće, kao što je i inače moguće da metode pozivaju jedna drugu), rešićemo problem:

C#
C++
Java
Python
	                            	
public class Pravougaonik
{
	public Double a, b, O, P;
	
	public Pravougaonik(Double a, Double b)
	{
		this.a = a;
		this.b = b;
		RacunanjeObima();
		RacunanjePovrsine();
	}
	
	public void RacunanjeObima()
	{
		O = 2 * (a + b);
	}
	
	public void RacunanjePovrsine()
	{
		P = a * b;
	}
}
									
	                            
	                            	
class Pravougaonik
{
	public:

	double a, b, O, P;
	
	Pravougaonik(double a, double b)
	{
		this.a = a;
		this.b = b;
		RacunanjeObima();
		RacunanjePovrsine();
	}
	
	void RacunanjeObima()
	{
		O = 2 * (a + b);
	}
	
	void RacunanjePovrsine()
	{
		P = a * b;
	}
};
									
	                            
	                            	
public class Pravougaonik {
	public double a, b, O, P;
	
	public Pravougaonik(double a, double b) {
		this.a = a;
		this.b = b;
		RacunanjeObima();
		RacunanjePovrsine();
	}
	
	public void RacunanjeObima() {
		O = 2 * (a + b);
	}
	
	public void RacunanjePovrsine()	{
		P = a * b;
	}
}
									
	                            
	                            	
class Pravougaonik
	a, b, O, P
	
def __init__ (self, a, b):
		self.a = a
		self.b = b
		RacunanjeObima()
		RacunanjePovrsine()

def RacunanjeObima():
	O = 2 * (a + b)

def RacunanjePovrsine():
	P = a * b
									
	                            
Slika 9. - Klasa Pravougaonik dopunjena metodama za računanje obima i površine, koje pozivamo u konstruktoru.

Sada će (konačno), pri sledećem pozivu ....

C#
C++
Java
Python
	                            	
Pravougaonik P = new Pravougaonik(12.55, 10.40);
									
	                            
	                            	
Pravougaonik P(12.55, 10.40);
									
	                            
	                            	
Pravougaonik P = new Pravougaonik(12.55, 10.40);
									
	                            
	                            	
P = new Pravougaonik(12.55, 10.40)
									
	                            
Slika 10. - Ponovo pozivamo konstruktor klase.

.... objekat biti korektno inicijalizovan.

Pri pozivu konsturktora, pozivaju se i metode za računanje obima i površine, te će stoga sva četiri polja dobiti korektne vrednosti.

Ako pozovemo sledeći programski kod ....

C#
C++
Java
Python
	                            	
Console.WriteLine("-Stranica a: " + P.a.ToString());
Console.WriteLine("-Stranica b: " + P.b.ToString());
Console.WriteLine("-Obim: "       + P.O.ToString());
Console.WriteLine("-Površina: "   + P.P.ToString());
									
	                            
	                            	
cout << "-Stranica a: " << P.a;
cout << "-Stranica b: " << P.b;
cout << "-Obim: "       << P.O;
cout << "-Površina: "   << P.P;
									
	                            
	                            	
System.out.printf("-Stranica a: %f", P.a);
System.out.printf("-Stranica b: %f", P.b);
System.out.printf("-Obim: %f",       P.O);
System.out.printf("-Površina: %f",   P.P);
									
	                            
	                            	
print("-Stranica a: " + str(P.a.ToString))
print("-Stranica b: " + str(P.b.ToString))
print("-Obim: "       + str(P.O.ToString))
print("-Površina: "   + str(P.P.ToString))
									
	                            
Slika 11. - Konzolne instrukcije za ispis vrednosti polja.

.... dobićemo sledeći ispis:

	                            	
-Stranica a: 12.55
-Stranica b: 10.40
-Obim: 45.90
-Površina: 130.52
									
	                            
Slika 12. - Rezultat izvršavanja prethodno navedenih instrukcija.

Konačno smo došli do toga da je (bar) početno stanje objekta pravilno zadato, a, ako nam se ne sviđa prethodni poziv, možemo kreirati zasebnu metodu za ispis vrednosti polja ....

C#
C++
Java
Python
	                            	
public class Pravougaonik
{
	public Double a, b, O, P;
	
	public Pravougaonik(Double a, Double b)
	{
		this.a = a;
		this.b = b;
		RacunanjeObima();
		RacunanjePovrsine();
	}
	
	public void RacunanjeObima()
	{
		O = 2 * (a + b);
	}
	
	public void RacunanjePovrsine()
	{
		P = a * b;
	}
	
	public void KonzolniIspis()
	{
		Console.WriteLine("-Stranica a: " + this.a.ToString());
		Console.WriteLine("-Stranica b: " + this.b.ToString());
		Console.WriteLine("-Obim: "       + this.O.ToString());
		Console.WriteLine("-Površina: "   + this.P.ToString());
	}
}
									
	                            
	                            	
class Pravougaonik
{
	public:

	double a, b, O, P;
	
	Pravougaonik(double a, double b)
	{
		this.a = a;
		this.b = b;
		RacunanjeObima();
		RacunanjePovrsine();
	}
	
	void RacunanjeObima()
	{
		O = 2 * (a + b);
	}
	
	void RacunanjePovrsine()
	{
		P = a * b;
	}
	
	void KonzolniIspis()
	{
		cout << "-Stranica a: " << this.a << endl;
		cout << "-Stranica b: " << this.b << endl;
		cout << "-Obim: "       << this.O << endl;
		cout << "-Površina: "   << this.P << endl;
	}
};
									
	                            
	                            	
public class Pravougaonik {
	public double a, b, O, P;
	
	public Pravougaonik(double a, double b)	{
		this.a = a;
		this.b = b;
		RacunanjeObima();
		RacunanjePovrsine();
	}
	
	public void RacunanjeObima() {
		O = 2 * (a + b);
	}
	
	public void RacunanjePovrsine()	{
		P = a * b;
	}
	
	public void KonzolniIspis()	{
		System.out.printf("-Stranica a: %f", this.a);
		System.out.printf("-Stranica b: %f", this.b);
		System.out.printf("-Obim: %f",       this.O);
		System.out.printf("-Površina: %f",   this.P);
	}
}
									
	                            
	                            	
class Pravougaonik
	a, b, O, P
	
	def __init__(self, a, b):
		self.a = a
		self.b = b
		RacunanjeObima()
		RacunanjePovrsine()
	
def RacunanjeObima():
	O = 2 * (a + b)	

def RacunanjePovrsine():
	P = a * b
	
def KonzolniIspis():
	print("-Stranica a: " + str(self.a))
	print("-Stranica b: " + str(self.b))
	print("-Obim: "       + str(self.O))
	print("-Površina: "   + str(self.P))
									
	                            
Slika 13. - Klasa Pravougaonik sa dodatom metodom za konzolni ispis vrednosti polja.

.... i pozvati je na sledeći način (koji je svakako nešto elegantniji):

C#
C++
Java
Python
	                            	
P.KonzolniIspis();
									
	                            
	                            	
P.KonzolniIspis();
									
	                            
	                            	
P.KonzolniIspis();
									
	                            
	                            	
P.KonzolniIspis()
									
	                            
Slika 14. - Pozivanje metode za konzolni ispis.

Programski kod sada jeste mnogo pregledniji, ali, definicija klase još uvek nije potpuna ....

Iako dosadašnji zapis omogućava korektnu inicijalizaciju objekta, ne postoji (za sada) mehanizam koji omogućavaju automatsko ažuriranje obima i površine pri promeni vrednosti stranica, kao i mehanizmi koji onemogućavaju unošenje pogrešnih (recimo - negativnih) vrednosti stranica.

Skup tehnika OOP-a koje omogućavaju ovakve "sigurnosne" provere nose naziv - enkapsulacija, što će biti glavna tema u nastavku, ali ćemo se, pre nego što se posvetimo enkapsulaciji, vratiti na rezervisanu reč this koju smo prethodno koristili unutar konstruktora (a detaljnije objašnjenje smo tada ostavili za kasnije).

Rezervisana reč "this"

Svrha rezervisane reči this je razrešavanje nedoumica koje mogu nastati u zapisu programskog koda, kada parametri konstruktora imaju iste nazive kao polja klase.

Vrlo je uobičajeno da parametri konsturktora (koji se praktično koriste samo pri inicijalizaciji objekata), imaju iste nazive kao polja klase.

Da pojasnimo ....

Kada je objekat kreiran, njegovim poljima se pristupa navođenjem identifikatora objekta i polja, koji su spojeni operatorom pristupa (. u C#-u i Javi ili -> u C++ - u)

(Primer: P.a - kao način pristupa stranici a objekta P).

Takav pristup je moguć kad (ako) je objekat već kreiran, ali, u samoj klasi Pravougaonik nije moguće koristiti poziv "Pravougaonik.a"!

Rezervisana reč this se koristi u situacijama kada u klasi želimo da se obratimo "budućem" objektu (koji tek treba da nastane upotrebom klase). U primeru konstruktora koji smo koristili....

C#
C++
Java
Python
	                            	
public Pravougaonik(Double a, Double b)
{
	this.a = a;
	this.b = b;
	RacunanjeObima();
	RacunanjePovrsine();
}
									
	                            
	                            	
public:

Pravougaonik(double a, double b)
{
	this.a = a;
	this.b = b;
	RacunanjeObima();
	RacunanjePovrsine();
};
									
	                            
	                            	
public Pravougaonik(double a, double b) {
	this.a = a;
	this.b = b;
	RacunanjeObima();
	RacunanjePovrsine();
}
									
	                            
	                            	
def __init__(self, a, b):
	self.a = a
	self.b = b
	RacunanjeObima()
	RacunanjePovrsine()
									
	                            
Slika 15. - Preko rezervisane reči "this" obraćamo se poljima klase na mestima gde može doći do dvoumljenja.

.... upotrebom rezervisane reči this pravimo razliku između polja klase a (this.a) i parametra konstruktora a.

U Python-u, umesto rezervisene reči this, koristi se rezervisana reč self.

Enkapsulacija

Enkapsulacija je skup tehnika kojima se omogućava pravilan pristup podacima, uspostavljanje veza između podataka i ažuriranje podataka u svim situacijama kada je to neophodno.

Ali takođe, u još opštijem (ili, prostije rečeno, osnovnijem) smislu, enkapsulacija u objektno orijentisanom programiranju je princip koji nalaže da se pristup podacima mora regulisati tako da "unutrašnji mehanizmi" klasa treba da budu skriveni od krajnjih korisnika, ali - tako da se korisnicima klase omogući neometano korišćenje svih neophodnih funkcionalnosti.

Da pojasnimo to preko primera: vozač automobila (u prenesenom značenju - krajnji korisnik klase), na raspolaganju ima volan, menjač i papučice za gas, kvačilo i kočnicu, čime je u mogućnosti da upravlja automobilom, ali, nema uticaja na dizajn karburatora, prenosnog mehanizma, vešanja, klipova i drugih delova, već je to posao autoinženjera (u prenesenom značenju - programera koji su zaduženi za dizajn/održavanje klase).

Sa blagodetima enkapsulacije (koliko god to čudno delovalo) najbolje ćemo se upoznati dodatnim posmatranjem klase koju smo već koristili, koja (još uvek) ne koristi enkapsulaciju ....

Šta se dešava ukoliko ne koristimo enkapsulaciju?

Da li se sećamo naredbe P.a = 12.55 kojom smo zadali vrednost 12.55 polju a objekta P? Da li to znači da možemo napraviti i sledeće pozive (iako je objekat sada korektno inicijalizovan) ....

C#
C++
Java
Python
	                            	
P.O = 4114.14;
P.P = -19.25;
									
	                            
	                            	
P.O = 4114.14;
P.P = -19.25;
									
	                            
	                            	
P.O = 4114.14;
P.P = -19.25;
									
	                            
	                            	
P.O = 4114.14
P.P = -19.25
									
	                            
Slika 16. - Zadavanje nelogičnih vrednosti poljima  O i P (vrednosti su, same po sebi, kao podaci, tačne, ali, nisu u skladu sa vrednostima polja a i b).

.... i da li će oni ponovo poremetiti objekat?

Možemo izvršiti navedene naredbe, ali će one će očekivano dovesti objekat u stanje nesklada (nadamo se da ste bili spremni na ovakav ishod, iako je nepovoljan): obim je mnogo veći nego što bi trebalo, a površina je negativan broj.

Međutim, pre svega treba se zapitati - da li uopšte treba da postoji mogućnost direktnog zadavanja vrednosti obima i površine?!

Pitanje je jednostavno, a odgovor je - ne (matematika kaže da postoji beskonačno mnogo kombinacija vrednosti stranica a i b koje daju određeni obim ili površinu, a u praktičnoj implementaciji na računaru, iako taj broj nije beskonačan, kombinacija ima izrazito mnogo).

U tehničkom smislu, enkapsulacija se postiže kritičkom upotrebom specifikatora pristupa u kombinaciji sa javnim metodama koje regulišu pristup privatnim poljima (ili upotrebom akcesora set i get, u jezicima koji to podržavaju).

Upravo smo izneli podosta novih/nepoznatih pojmova, pa ćemo se u nastavku potruditi da ih sve detaljno razjasnimo (i naravno, iako će nam ove novopomenute tehnikalije malo "zakomplikovati život", konačno ćemo srediti klasu Pravougaonik kako dolikuje).

Specifikatori pristupa (private, public, protected)

Specifikatori pristupa su rezervisane reči koje stoje uz deklaracije polja i metoda i određuju mogućnost pristupa datim podacima:

  • private - poljima/metodama moguće je pristupati samo unutar klase i nije moguće pristupati im iz spoljnih delova koda
  • public - poljima/metodama moguće je neposredno pristupati iz spoljnih delova koda (i naravno unutar klase)
  • protected - poljima/metodama moguće je pristupati unutar osnovne klase i nasleđenih klasa (o nasleđivanju ćemo govoriti u nastavku), ali, nije im moguće pristupati im iz spoljnih delova koda

Najvažniji specifikatori pristupa su private i public, pa ćemo im posvetiti najviše pažnje.

Primetili smo da je, do sada, uz sva polja (promenljive) klase Pravougaonik stajala odrednica "public". Ova rezervisana reč sugeriše spoljnjim delovima programskog koda da mogu neposredno pristupati poljima uz koje ta odrednica stoji. Dobra strana ove "otvorenosti" je to što možemo neposredno pristupati poljima, a loša, to što (time što bilo kom polju koje ima specifikator pristupa public možemo neposredno pristupiti), možemo dovesti u pitanje korektnost međusobne povezanosti polja i (samim tim) funkcionalnost objekta (kao recimo malopre, kada smo upisali da je površina pravougaonika -19.25, što nije matematički moguće).

U Python-u se, umesto eksplicitnog navođenja specifikatora pristupa, koristi način imenovanja promenljivih koji podrazumeva da:

  • public promenljive nemaju prefiks sačinjen iz donjih crta (primer: a, b)
  • identifikatori protected promenljivih počinju jednom donjom crtom (primer: _a, _b)
  • identifikatori private promenljivih počinju sa dve donje crte (primer: __a, __b)

Prvi korak u enkapsulaciji je da, preko specifikatora pristupa private onemogućimo neposredan pristup određenom polju. U klasi Pravougaonik, to bi izgledalo ovako:

C#
C++
Java
Python
	                            	
private Double a, b, O, P;
									
	                            
	                            	
private:

double a, b, O, P;
									
	                            
	                            	
private double a, b, O, P;
									
	                            
	                            	
__a, __b, __O, __P
									
	                            
Slika 17. - Korišćenjem specifikatora pristupa private, skrivamo polja klase. U sledećem koraku kreiraćemo metode kojima se kontroliše pristup datim vrednostima.

Međutim, kada to uradimo, spoljni delovi koda više ne mogu pristupati poljima a, b, O i P!

Ovo može, ali i ne mora, predstavljati problem (bićemo praktični: u našem slučaju, predstavlja problem).

Ako (u opštem slučaju) nemogućnost pristupa poljima ne predstavlja problem, ne moramo preduzimati dalje korake.

Budući da definicija klase Pravougaonik sadrži metodu za ispis vrednosti, moglo bi se desiti da je to sve što nam je potrebno, međutim, razumno je ipak pretpostaviti da će bar povremeno postojati potreba za ažuriranjem stranica pravougaonika, i pogotovo - za neposrednim čitanjem vrednosti pojedinačnih polja.

Za početak, pogledaćemo u sledećem odeljku na koji način možemo privatnim poljima pristupati preko javnih metoda, a u nastavku ćemo se pozabaviti i pristupom privatnim poljima upotrebom "svojstava" (property), preko (takozvanih) akcesora "set" i "get".

Pristup privatnim poljima preko javnih metoda

Javna metoda za čitanje vrednosti privatnog polja ima sledeći oblik:

C#
C++
Java
Python
	                            	
public Double Citanje_a()
{
	return this.a;
}
									
	                            
	                            	
public:

double Citanje_a()
{
	return this.a;
};
									
	                            
	                            	
public double Citanje_a() {
	return this.a;
}
									
	                            
	                            	
def Citanje_a():
	return self.a
									
	                            
Slika 18. - Javno dostupna (publici) metoda Citanje_a() vraća vrednost polja a.

Specifikator pristupa public omogućava pristup metodi iz spoljnih delova koda, a preko povratnog tip Double omogućava se direktno čitanje vrednosti polja a u izvornom obliku (a ne samo tekstualni ispis).

Za upis (ažuriranje), vrednosti polja a, koristićemo sledeću javnu metodu:

C#
C++
Java
Python
	                            	
public void Upis_a(Double a)
{
	// Radi preglednosti nećemo ovde
	// pisati deo koda sa porukom o grešci,
	// koji inače možemo upotrebiti

	if (a <= 0) return;  
						   
	// Ako je uneta vrednost pozitivan broj,
	// ažuriraćemo obim i površinu

	this.a = a;          
	RacunanjeObima();
	RacunanjePovrsine();
}
									
	                            
	                            	
public:

void Upis_a(double a)
{
	// Radi preglednosti nećemo ovde
	// pisati deo koda sa porukom o grešci,
	// koji inače možemo upotrebiti

	if (a <= 0) return;  
						   
	// Ako je uneta vrednost pozitivan broj,
	// ažuriraćemo obim i površinu

	this.a = a;          
	RacunanjeObima();
	RacunanjePovrsine();
};
									
	                            
	                            	
public void Upis_a(double a) {
	// Radi preglednosti nećemo ovde
	// pisati deo koda sa porukom o grešci,
	// koji inače možemo upotrebiti

	if (a <= 0) return;  
						   
	// Ako je uneta vrednost pozitivan broj,
	// ažuriraćemo obim i površinu

	this.a = a;          
	RacunanjeObima();
	RacunanjePovrsine();
}
									
	                            
	                            	
def Upis_a(a)
	# Radi preglednosti nećemo ovde
	# pisati deo koda sa porukom o grešci,
	# koji inače možemo upotrebiti

	if a <= 0:
		return  
						   
	# Ako je uneta vrednost pozitivan broj,
	# ažuriraćemo obim i površinu

	self.a = a
	RacunanjeObima()
	RacunanjePovrsine()
									
	                            
Slika 19. - Javno dostupna (public) metoda Upis_a upisuje predatu vrednost u polje a (ali, samo ako je data vrednost pozitivna) i potom ažurira obim i površinu.

Upotrebom ovakvog pristupa rešavamo dva problema:

  • Proveru ulaznog podatka (ukoliko je unet negativni broj, vrednost polja a se ne ažurira, a metode za ažuriranje obima i površine se ne pozivaju)
  • Ažuriranje obima i površine pri zadavanju nove vrednosti stranice

Klasa je sada konačno spremna za upotrebu, pa možemo pogledati šematske prikaze klase koju smo napisali i objekta koji nastaje upotrebom date klase:

Klasa ....

Šematski prikaz klase Pravougaonik
Slika 20. - Šematski prikaz klase pravougaonik (konstruktor je izostavljen zarad preglednosti)

Objekat ....

Šematski prikaz objekta P (klase Pravougaonik)
Slika 21. - Spolja gledano, objekat nudi samo nekoliko metoda i ne prikazuje unutrašnje mehanizme obrade podataka

Primećujemo da klasa naizgled sadrži više. Međutim, to je samo naizgled. Na objektu je takođe sve tu, samo .... "ispod haube". :)

Ovo je (takođe) šematski prikaz principa enkapsulacije: "sve je tu", ali, direktno možemo pristupati samo određenim matodama.

Za kraj odeljka o enkapsulaciji, razmotrimo drugi primer enkapsulacije: primer upotrebe svojstava u programskom jeziku C# (što je pristup kojim se postiže još veća preglednost, ali, nažalost nije dostupan u ostalim jezicima).

Enkapsulacija upotrebom akcesora u programskom jeziku C#

Videli smo da je pristup privatnim poljima moguć preko javnih (public) metoda, a da je razlog zašto uopšte skrivamo polja klase to što, u mnogim situacijama (gde su vrednosti određenih polja uslovljene vrednostima drugih polja) želimo da očuvamo veze između tih podataka (enkapsulacija i skrivanje podataka nisu uvek obavezni, ali, u velikom broju situacija postoji potreba za takvim pristupom).

Ovakav pristup možemo koristiti u većini programskih jezika koji podržavaju OOP pristup, ali, neki jezici, kao što je C# koji koristimo u ovom članku, podržavaju i upotrebu takozvanih "svojstava", posebnih blokova programskog koda koji sadrže takozvane akcesore, kojima se omogućava uslovni pristup skrivenim poljima.

Ovde zapravo mislimo na isti pristup kakav smo već koristili u prethodnom odeljku (ali, zapisan na nešto elegantniji način) i sve je zapravo mnogo jednostavnije nego što deluje na prvi pogled.

Da bismo se uverili u to, pogledaćemo jednostavan primer (pri čemu ćemo napraviti i izvesne male izmene i prikazati odmah kako upotreba svojstava obično izgleda u klasama):

C#
C++
Java
Python
	                            	
public class Pravougaonik
{
	private Double _a, _b, _O, _P;

	public Pravougaonik(Double a, Double b)
	{
		this.a = a;
		this.b = b;
		RacunanjeObima();
		RacunanjePovrsine();
	}

	public Double a // Ovo je "svojstvo" a (nije isto što i polje a,
	{               // kao što smo imali u prethodnom opisu klase)

		get
		{
			return _a;

		}
		set
		{
			if(a > 0)
			{
				this.a = value;
				RacunanjeObima();
				RacunanjePovrsine();
			}
		}

	}                
}
									
	                            
	                            	
// Opcija nije dostupna u programskom jeziku C++
									
	                            
	                            	
// Opcija nije dostupna u programskom jeziku Java
									
	                            
	                            	
# Opcija nije dostupna u programskom jeziku Python
									
	                            
Slika 22. - Svostvo a, definisano preko dva akcesora (set i get). Svojstva su svojevrsne kombinacije polja i metoda: spolja gledano, izgledaju kao public polja, a, po potrebi, funkcionišu kao metode.

Da pojasnimo šta je to što smo videli:

Deo koda ....

C#
C++
Java
Python
	                            	
public Double a
{

}
									
	                            
	                            	
// Opcija nije dostupna u programskom jeziku C++
									
	                            
	                            	
// Opcija nije dostupna u programskom jeziku Java
									
	                            
	                            	
# Opcija nije dostupna u programskom jeziku Python
									
	                            
Slika 23. - Osnovna definicija svojstva a.

.... definiše svojstvo a koje će biti dostupno spoljnim delovima koda (specifikator pristupa ovog svojstva je public).

Unutar zagrada, definisana su dva bloka. Prvi blok je get ....

C#
C++
Java
Python
	                            	
get
{
	return _a
}
									
	                            
	                            	
// Opcija nije dostupna u programskom jeziku C++
									
	                            
	                            	
// Opcija nije dostupna u programskom jeziku Java
									
	                            
	                            	
# Opcija nije dostupna u programskom jeziku Python
									
	                            
Slika 24. - Akcesor get koji vraća vrednost polja _a.

.... kojim je određeno da će programski kod koji zatraži vrednost svojstva a (zapravo) dobiti vredost polja _a. Na primer, preko sledećeg koda ....

C#
C++
Java
Python
	                            	
Pravougaonik P = new Pravougaonik(5.5, 10.5);
MessageBox.Show("Stranica a: " + P.a.ToString());
									
	                            
	                            	
// Opcija nije dostupna u programskom jeziku C++
									
	                            
	                            	
// Opcija nije dostupna u programskom jeziku Java
									
	                            
	                            	
# Opcija nije dostupna u programskom jeziku Python
									
	                            
Slika 25. - Primer koda koji prikazuje upotrebu svojstva a (naizgled, veoma slično kao kada smo samo koristili javno polje a, ali - samo naizgled).

.... dobićemo ispis stranice a. Pozivom P.a dobijamo vrednost polja P._a (a zatim se poziva opšta funkcija ToString() kojom se ispisuje vrednost).

Drugi blok je set ....

C#
C++
Java
Python
	                            	
set
{
	if(a > 0)
	{
		this.a = value;
		RacunanjeObima();
		RacunanjePovrsine();
	}
}
									
	                            
	                            	
// Opcija nije dostupna u programskom jeziku C++
									
	                            
	                            	
// Opcija nije dostupna u programskom jeziku Java
									
	                            
	                            	
# Opcija nije dostupna u programskom jeziku Python
									
	                            
Slika 26. - Upravo je akcesor set ono što čini svojstvo a suštinski različitim (i mnogo "sigurnijim" za korišćenje), u odnosu na javno polje a (koje smo koristili pre nego što smo počeli da upotrebljavamo svojstva). Ako sada pokušamo da unesemo vredsnot stranice koja je manja od 0, program neće prihvatiti novu vrednost (i neće preračunavati bim i površinu).

.... kojim je određeno da će polje _a dobiti predatu vrednost (value), samo u slučaju ako je predata vrednost veća od nule.

Prvo što primećujemo u odeljku set je sličnost sa pristupom koji smo koristili u prethodnom odeljku (gde smo koristili metodu Upis_a), a drugo je rezervisana reč value.

Posmatrajmo to ovako (postavimo sebi pitanje): pod kojim okolnostima određeno polje može da promeni vrednost? Odgovor je - preko naredbe dodele, a naredbe dodele, sa jedne strane imaju identifikator promenljive (može naravno biti i polje klase), a sa druge .... ništa drugo nego izračunatu vrednost.

C#
C++
Java
Python
	                            	
P.a = 10;    // vrednost se zadaje direktno
P.a = X;     // vrednost se dobija čitanjem vrednost druge promenljive
P.a = X + Y; // vrednost se dobija računanjem vrednosti izraza
									
	                            
	                            	
P.a = 10;    // vrednost se zadaje direktno
P.a = X;     // vrednost se dobija čitanjem vrednost druge promenljive
P.a = X + Y; // vrednost se dobija računanjem vrednosti izraza
									
	                            
	                            	
P.a = 10;    // vrednost se zadaje direktno
P.a = X;     // vrednost se dobija čitanjem vrednost druge promenljive
P.a = X + Y; // vrednost se dobija računanjem vrednosti izraza
									
	                            
	                            	
P.a = 10;    # vrednost se zadaje direktno
P.a = X;     # vrednost se dobija čitanjem vrednost druge promenljive
P.a = X + Y; # vrednost se dobija računanjem vrednosti izraza
									
	                            
Slika 27. - Primeri različitih naredbi dodele.

Upravo to je razlog zašto se (makar u programskom eziku C#), koristi rezervisana reč value, u smislu: kakva god vrednost da se nađe sa desne strane naredbe dodele, biće prosleđena svojstvu a.

Na kraju, primetimo da su svojstva svojevrsne kombinacije polja i metoda. Svojstvo, spolja gledano (kada svojstvu pristupamo iz spoljnih delova koda), izgleda kao polje, ali, "iznutra" funkcioniše kao metoda. U našem slučaju (koji je tipičan primer upotrebe svojstava):

  • dodela nije bezuslovna
  • posle dodele pozivaju se metode za ažuriranje

.... a takođe, u opštem smislu:

  • jedan od dva bloka može se i izostaviti

Na primer:

C#
C++
Java
Python
	                            	
public class Pravougaonik
{
	....

	public Double O // Obim 
	{
		get
		{
			return _O;

		}
	}

	public Double P // Površina
	{
		get
		{
			return _P;

		}
	}                
}
									
	                            
	                            	
// Opcija nije dostupna u programskom jeziku C++
									
	                            
	                            	
// Opcija nije dostupna u programskom jeziku Java
									
	                            
	                            	
# Opcija nije dostupna u programskom jeziku Python
									
	                            
Slika 28. - Svojstva O i P omogućavaju korisnicima klase da očitavaju vrednosti za obim i površinu, ali ne i da zadaju ove vrednosti direktno (što je svakako dobra praksa).

Svojstva O i P su "read only". Moguće je samo čitati vrednosti obima i površine (da li zaista ikako možemo odrediti stranice a i b ako unesemo vrednost obima ili površine?!).

Takođe, primetili smo i da ćemo sada, kada spoljnim delovima koda "otkrivamo" svojstva koja nose prethodne nazive polja (a, b, O i P), za sama polja koristiti nazive sa dodatkom podvučene crte (_a, _b, _O i _P). Ovo je stvar konvencije i uobičajena praksa u programiranju koju svakako treba da uvažimo.

Nasleđivanje i polimorfizam

Pred kraj, pozabavićemo se temom nasleđivanja u objektno orijentisanom programiranju.

Rekli smo da je (prava) svrha objektno orijentisanog programiranja dobra organizacija podataka.

U tom smislu, da bismo razumeli svrhu nasleđivanja u OOP-u, zamislićemo sledeću situaciju (nećemo ovoga puta koristiti klasu pravougaonik, već ćemo uzeti drugu klasu za primer):

  • Potrebno je definisati klasu Osoba, sa nekoliko opštih polja kao štu Ime, Prezime, DatumRodjenja i Starost
  • Potrebno je definisati klasu Radnik koja sadrži sva svojstva prethodne klase i pri tom sadrži polja koja su specifično vezana za radnike: RadnoMesto, PocetakRada i RadniStaz

Možemo definisati dve potpuno nezavisne klase, ali, možemo primeniti i bolji pristup koji omogućava određene pogodnosti.

Nasleđivanje

Prvo ćemo definisati opštiju od dve klase (onu koja sadrži zajedničke podatke), što je u našem slučaju klasa Osoba:

C#
C++
Java
Python
	                            	
public class Osoba
{
	public  String   Ime, Prezime;
	public  DateTime DatumRodjenja;
	private UInt32   _Starost;

	public Osoba(String Ime, String Prezime, DateTime DatumRodjenja)
	{
		this.Ime           = Ime;
		this.Prezime       = Prezime;
		this.DatumRodjenja = DatumRodjenja;

		RacunanjeStarosti();
	}

	public void RacunanjeStarosti()
	{
		_Starost = DateTime.Now.Year - DatumRodjenja.Year;
		
		if(DateTime.Now.Month < DatumRodjenja.Month)
		{
			_Starost--;
		}

		if(DateTime.Now.Month == DatumRodjenja.Month &&
		   DateTime.Now.Day   <  DatumRodjenja.Day)
		{
			_Starost--;
		}

	}

	public UInt32 Starost
	{
		return _Starost;
	}
}
									
	                            
	                            	
struct Datum {
	int godina, mesec, dan;
};

class Osoba
{
	private:
		
	int _starost;
		
	public:
	
	string ime, prezime;
	Datum  datumRodjenja;
	
	Osoba(string ime, string prezime, Datum datumRodjenja)
	{
		this->ime           = ime;
		this->prezime       = prezime;
		this->datumRodjenja = datumRodjenja;
		racunanjeStarosti();
	}
	
	racunanjeStarosti()
	{
		time_t d     = time(NULL);
		tm*    datum = localtime(&d);
		
		int godina   = datum->tm_year + 1900;
		int mesec    = datum->tm_mon  + 1;
		int dan      = datum->tm_mday;
		
		_starost = godina - datumRodjenja.godina;
		
		if(mesec < datumRodjenja.mesec)
		{
			_starost--;
		}

		if(mesec == datumRodjenja.mesec &&
		   dan   <  datumRodjenja.dan)
		{
			_starost--;
		}
	}
	
	int citanjeStarosti() {
		return _starost;
	}
};
									
	                            
	                            	
public class Osoba {
	public  String    Ime, Prezime;
	public  LocalDate DatumRodjenja;
	private int       _Starost;
	
	public Osoba(String Ime, String Prezime, LocalDate DatumRodjenja) {
		this.Ime           = Ime;
		this.Prezime       = Prezime;
		this.DatumRodjenja = DatumRodjenja;

		RacunanjeStarosti();
	}

	public void RacunanjeStarosti()	{
		LocalDate datum = LocalDate.now();
		_Starost = datum.getYear() - DatumRodjenja.getYear();
		
		if(datum.getMonthValue() < DatumRodjenja.getMonthValue()) {
			_Starost--;
		}

		if(datum.getMonthValue() == DatumRodjenja.getMonthValue() &&
		   datum.getDayOfMonth() <  DatumRodjenja.getDayOfMonth()) {
			_Starost--;
		}

	}

	public int citanjeStarosti() {
		return _Starost;
	}
}
									
	                            
	                            	
import datetime.datetime

class Osoba:

	def __init__(self, ime, prezime, datumRodjenja):
		self.ime           = ime
		self.prezime       = prezime
		self.datumRodjenja = datumRodjenja
		self.racunanjeStarosti()

	def racunanjeStarosti(self):
		datum         = datetime.datetime.now()
		self.__starost = datum.year - self.datumRodjenja.year

		if datum.month < self.datumRodjenja.month:
			self.__starost = self.__starost - 1

		if datum.month == self.datumRodjenja.month and
		   datum.day  <  self.datumRodjenja.day:
			self.__starost = self.__starost - 1

	def citanjeStarosti(self):
		return self.__starost
									
	                            
Slika 29. - Osnovna klasa Osoba, iz koje će u nastavku biti izvedena klasa Radnik.

Vidimo da ovakva klasa nema baš previše podataka, ali, čak i na ovakvom veoma jednostavnom primeru, vidimo da bi definisanje klase Radnik u kojoj bismo bukvalno "prepisali" prethodni kod, ne deluje kao najpraktičnije rešenje.

Klasa Radnik nalsediće klasu Osoba (koristi polja/metode/svojstva ove klase), pri čemu ćemo naravno biti u mogućnosti da dodamo nova polja, metode i svojstva.

C#
C++
Java
Python
	                            	
public class Radnik : Osoba
{
	private String   _RadnoMesto;
	private DateTime _PocetakRada;
	private UInt32   _RadniStaz;

	public Radnik(String Ime, String Prezime,
	              String DatumRodjenja, String RadnoMesto,
	              DateTime PocetakRada) : base(Ime, Prezime, DatumRodjenja)
	{
		this._RadnoMesto    = RadnoMesto;
		this._DatumRodjenja = DatumRodjenja;
		this._PocetakRada   = PocetakRada;

		RacunanjeStaza();
	}

	public void RacunajeStaza()
	{
		_RadniStaz = DateTime.Now.Year - PocetakRada.Year;
		
		if(DateTime.Now.Month < PocetakRada.Month)
		{
			_RadniStaz--;
		}

		if(DateTime.Now.Month == PocetakRada.Month &&
		   DateTime.Now.Day   <  PocetakRada.Day)
		{
			_RadniStaz--;
		}
	}

	public UInt32 RadnoMesto
	{
		get
		{
			return _RadnoMesto;
		}
	}

	public UInt32 RadniStaz
	{
		get
		{
			return _RadniStaz;
		}
	}
}
									
	                            
	                            	
class Radnik : public Osoba {
	
	private:
	
	Datum  _pocetakRada;
	string _pozicija; 
	int    _staz;
	
	public:
	
	Radnik(string ime, string prezime, Datum datumRodjenja, string pozicija, Datum pocetakRada) : Osoba(ime, prezime, datumRodjenja)
	{
		this->_pozicija    = pozicija;
		this->_pocetakRada = pocetakRada;
		racunanjeStaza();
	}
	
	racunanjeStaza()
	{
		time_t d     = time(NULL);
		tm*    datum = localtime(&d);
		
		int godina   = datum->tm_year + 1900;
		int mesec    = datum->tm_mon  + 1;
		int dan      = datum->tm_mday;
		
		_staz = godina - _pocetakRada.godina;
		
		if(mesec < _pocetakRada.mesec)
		{
			_staz--;
		}

		if(mesec == _pocetakRada.mesec &&
		   dan   <  _pocetakRada.dan)
		{
			_staz--;
		}
	}
	
	int citanjeStaza()
	{
		return _staz;
	}
	
	string citanjePozicije()
	{
		return _pozicija;
	}
};

									
	                            
	                            	
public class Radnik extends Osoba {

	private  String    Pozicija;
	private  int       _Staz;
	public   LocalDate PocetakRada;
	
	public Radnik(String Ime, String Prezime, LocalDate DatumRodjenja, String Pozicija, LocalDate PocetakRada) {
		super(Ime, Prezime, DatumRodjenja);
		this.PocetakRada = PocetakRada;
		this.Pozicija    = Pozicija;
		RacunanjeStaza();
	}
	
	public void RacunanjeStaza( ) {
		LocalDate datum = LocalDate.now();
		_Staz = datum.getYear() - PocetakRada.getYear();
		
		if(datum.getMonthValue() < PocetakRada.getMonthValue()) {
			_Staz--;
		}

		if(datum.getMonthValue() == PocetakRada.getMonthValue() &&
		   datum.getDayOfMonth() <  PocetakRada.getDayOfMonth()) {
			_Staz--;
		}
	}
	
	public int citanjeStaza() {
		return _Staz;
	}

	public int citanjePozicije() {
		return _Pozicija;
	}
}
									
	                            
	                            	
class Radnik(Osoba):

	def __init__(self, ime, prezime, datumRodjenja, radnoMesto, pocetakRada):
		self.radnoMesto  = radnoMesto
		self.pocetakRada = pocetakRada
		Osoba.__init__(self, ime, prezime, datumRodjenja)
		self.racunanjeRadnogStaza()

	def racunanjeRadnogStaza(self):
		datum            = datetime.datetime.now()
		self.__radniStaz = datum.year - self.pocetakRada.year

		if datum.month < self.pocetakRada.month:
			self.__radniStaz = self.__radniStaz - 1

		if datum.month == self.pocetakRada.month and
		   datum.day  <  self.pocetakRada.day:
			self.__radniStaz = self.__radniStaz - 1

	def citanjeStaza(self):
		return self.__radniStaz
									
	                            
Slika 30. - Klasa Radnik, koja nasleđuje klasu Osoba, to jest, preuzima njena polja, metode i svojstva, uz (naravno) mogućnost dodavanja novih.

Da pojasnimo: kada napišemo ....

C#
C++
Java
Python
	                            	
public class Radnik : Osoba
{

}
									
	                            
	                            	
class Radnik : public Osoba
{

};
									
	                            
	                            	
public class Radnik extends Osoba {

}
									
	                            
	                            	
class Radnik(Osoba):
									
	                            
Slika 31. - Osnovni programski kod kojim se definiše nasleđivanje klase.

.... definisali smo klasu Radnik koja nasleđuje klasu osoba. To znači da klasa Radnik ima sva polja, metode i svojstva koje ima klasa Osoba, pri čemu je moguće dodavati nove, svojstvene (samo) klasi radnik, kao na primer u konstruktoru ....

C#
C++
Java
Python
	                            	
public Radnik(String Ime, String Prezime, String DatumRodjenja,
              DateTime PocetakRada) : base(Ime, Prezime, DatumRodjenja)
{
	this._RadnoMesto    = RadnoMesto;
	this._DatumRodjenja = DatumRodjenja;
	this._PocetakRada   = PocetakRada;

	RacunanjeStaza();
}
									
	                            
	                            	
Radnik(string ime, string prezime, Datum datumRodjenja, string pozicija,
       Datum pocetakRada) : Osoba(ime, prezime, datumRodjenja)
{
	this->_pozicija    = pozicija;
	this->_pocetakRada = pocetakRada;
	racunanjeStaza();
}
									
	                            
	                            	
public Radnik(String Ime, String Prezime, String DatumRodjenja,
              String RadnoMesto, DateTime PocetakRada) {
	super(Ime, Prezime, DatumRodjenja);
	
	this._RadnoMesto    = RadnoMesto;
	this._PocetakRada   = PocetakRada;
	
	RacunanjeStaza();
}
									
	                            
	                            	
def __init__(self, ime, prezime, datumRodjenja, radnoMesto, pocetakRada):
	self.radnoMesto  = radnoMesto
	self.pocetakRada = pocetakRada
	Osoba.__init__(self, ime, prezime, datumRodjenja)
	self.racunanjeRadnogStaza()
									
	                            
Slika 32. - Konstruktor klase Radnik koji poziva konstruktor klase Osoba i takođe koristi sopstvena polja i pozive naredbi.

.... gde preko koda .....

C#
C++
Java
Python
	                            	
: base(Ime, Prezime, DatumRodjenja)
									
	                            
	                            	
: Osoba(Ime, Prezime, DatumRodjenja)
									
	                            
	                            	
super(Ime, Prezime, DatumRodjenja);
									
	                            
	                            	
Osoba.__init__(self, ime, prezime, datumRodjenja)
									
	                            
Slika 33. - Deo programskog koda kojim se konstruktor klase Radnik spaja sa konstruktorom klase Osoba.s

.... praktično pozivamo konstruktor klase Osoba, što podrazumeva inicijalizaciju tri navedena polja i pozivanje metode za računanje starosti, ali, budući da konstruktor klase Radnik sadrži i druge parametre i poziv metode za računanje radnog staža, vidimo da je povezivanje podataka koje smo ostvarili na ovaj način krajnje optimalno i praktično.

Polimorfizam

Sama reč polimorfizam (koja u prevodu označava "višeobličje"), u objektno orijentisanom programiranju označava da se osnovne klase i njihove izvedene klase u određenim okolnostima mogu koristiti ravnopravno, odnosno istovremeno (u nekim okolnostima - ali ne i uvek).

Pogledajmo jedan praktičan primer koji koristi klase koje smo već definisali.

Recimo, smatraćemo da dve klase koristimo za organizaciju podataka radnika sopstvene firme i članova njihove bliže familije. Podatke o radnicima ćemo beležiti preko klase Radnik, a podatke o ostalim osobama ćemo beležiti preko klase Osoba.

Sledeći zahtev je sastavljanje jedinstvene lista svih osoba, bez obzira na to da li su radnici, ili nisu (smatramo da je najbolje da postoji samo jedna takva lista, zarad dobre organizacije i uštede prostora). Recimo, potreban nam je način da pregledamo spisak svih osoba i (u određenim okolnostima) pronađemo:

  • decu mlađu od 7 godina (uoči Novogodišnjih praznika)
  • sve osobe starije od 55 godina (zarad organizacije karaoke večeri za sve seniore, bez obzira da li su radnici ili nisu)
  • sve žene (uoči 8. marta, radi dodele poklona, bez obzira da li su radnici ili nisu)

Naslućujete da je ovo sve moguće izvesti upotrebom tehnika OOP-a i da će nam pomenuti polimorfizam u tome "nekako" već pomoći.

Ako napravimo listu u kojoj su elementi objekti osnovne klase (Osoba) ....

C#
C++
Java
Python
	                            	
List<Osoba> SpisakOsoba = new List<Osoba>();
									
	                            
	                            	
list<Osoba> SpisakOsoba;
									
	                            
	                            	
List<Osoba> SpisakOsoba = new ArrayList<Osoba>();
									
	                            
	                            	
spisakOsoba = []
									
	                            
Slika 34. - Lista objekata klase Osoba. Na ovakvu listu moguće je stavljati ne samo objekte klase Osoba, već i objekte izvedenih klasa.

.... takva lista moći će da prima objekte klase Osoba i objekte klase Radnik. Na primer, sledeći kod ....

C#
C++
Java
Python
	                            	
Osoba osoba   = new Osoba("Dejana", "Marković", "1970-05-27");
SpisakOsoba.Add(osoba);

Radnik radnik = new Radnik("Petar", "Marković", "1969-11-21",
                           "Upravnik magacina", "1997-01-05");
SpisakOsoba.Add(radnik);
									
	                            
	                            	
Datum datumRodjenja;
Datum pocetakRada;
	
datumRodjenja.godina = 1970;
datumRodjenja.mesec  = 5;
datumRodjenja.dan    = 27;

Osoba osoba = Osoba("Dejana", "Marković", datumRodjenja);
SpisakOsoba.push_back(osoba);

datumRodjenja.godina = 1969;
datumRodjenja.mesec  = 11;
datumRodjenja.dan    = 21;

pocetakRada.godina = 1997;
pocetakRada.mesec  = 1;
pocetakRada.dan    = 5;

Radnik radnik = new Radnik("Petar", "Marković", datumRodjenja,
                           "Upravnik magacina", pocetakRada);
SpisakOsoba.push_back(radnik);
									
	                            
	                            	
LocalDate datumRodjenja1 = LocalDate.of(1970, 5, 27);
Osoba osoba              = new Osoba("Dejana", "Marković", datumRodjenja1);
SpisakOsoba.Add(osoba);

LocalDate datumRodjenja2 = LocalDate.of(1969, 11, 21);
LocalDate pocetakRada2   = LocalDate.of(1997, 1, 5);
Radnik radnik = new Radnik("Petar", "Marković", datumRodjenja2,
                           "Upravnik magacina", pocetakRada2);
SpisakOsoba.Add(radnik);
									
	                            
	                            	
datumRodjenja = datetime.datetime(1970, 5, 27)
osoba = Osoba("Dejana", "Marković", datumRodjenja)
spisakOsoba.append(osoba)


datumRodjenja = datetime.datetime(1969, 11, 21)
pocetakRada   = datetime.datetime(1997, 1, 5)
radnik        = Radnik("Petar", "Marković", datumRodjenja,
                     "Upravnik magacina", pocetakRada)
spisakOsoba.append(radnik)
									
	                            
Slika 35. - Primer dodavanja objekata osnovne klase (Osoba) i izvedene klase (Radnik) u listu.

.... izvršavaće se besprekorno.

Završićemo uz napomenu da je "obrnuti" pristup moguć (kreiranje liste objekata klase Radink nu koju bismo dodavali i objekte klase Osoba), ali, u tom slučaju moramo biti vrlo pažljivi. Recimo, sledeći kod ....

C#
C++
Java
Python
	                            	
Osoba O = SpisakOsoba.Peek();
									
	                            
	                            	
Osoba O = SpisakOsoba.front();
									
	                            
	                            	
Osoba O = SpisakOsoba.peek();
									
	                            
	                            	
O = spisakOsoba[len(spisakOsoba) - 1]
									
	                            
Slika 36. - Preuzimanje reference na objekat iz liste. Budući da se preuzeti objekat tretira kao objekat klase Osoba, nad njim je moguće primenjivati sve operacije.

.... daje referencu na objekat sa kojim možemo postupati na bilo koji (moguć) način, u tom smislu što objekat koij referenciramo garantovano ima sva polja, metode i svojstva koje bi nam moglo pasti na pamet da koristimo.

U obrnutim okolnostima (smatraćemo da smo kreirali listu objekata klase Radnik) ....

C#
C++
Java
Python
	                            	
Radnik R = SpisakRadnika.Peek();
									
	                            
	                            	
Radnik R = SpisakRadnika.front();
									
	                            
	                            	
Radnik R = SpisakRadnika.peek();
									
	                            
	                            	
R = spisakRadnika[len(spisakRadnika) - 1]
									
	                            
Slika 37. - Preuzimanje reference na objekat iz liste. Budući da se preuzeti objekat tretira kao objekat klase Radnik, moramo biti jako pažljivi i (zapravo) ne smemo pozivati metode koje nisu svojstvene klasi Osoba, da ne bi dolazilo do grešaka.

.... može doći do problema ukoliko preko reference R referenciramo objekat klase Osoba i pri tom (recimo) pozivamo neko od svojstava klase Radnik (recimo RadniStaz).

Ukoliko se ograničimo na polja/metode/svojstva koja su zajednička za obe klase (ili, za sve izvedene klase, u komplikovanijim slučajevima nasleđivanja), stvari će funkcionisati besprekorno.

Primer za kraj ....

Iako deluje pomalo nepotrebno posle svega što smo naveli, vratićemo se na pravougaonike i pogledati kako bi izgledao osnovni rad sa nizom pravougaonika koji su definisani preko klase (Pravougaonik), u poređenju sa proceduralnim pristupom gde se svojstva pravougaonika definišu preko zasebnih promenljivih:

Prvo koristimo OOP pristup:

C#
C++
Java
Python
	                            	
Pravougaonik[] Pravougaonici = new Pravougaonik[3];
									
	                            
	                            	
Pravougaonik[] Pravougaonici = new Pravougaonik[3];
									
	                            
	                            	
Pravougaonik[] Pravougaonici = new Pravougaonik[3];
									
	                            
	                            	
Pravougaonik[] Pravougaonici = new Pravougaonik[3]
									
	                            
Slika 38. - Kreiranje niza objekata klase pravougaonik.

Ako je potrebno da pronađemo veći među prva dva pravougaonika, ceo kod izgledaće ovako:

C#
C++
Java
Python
	                            	
Pravougaonik[] Pravougaonici = new Pravougaonik[3];
Pravougaonici[0] = new Pravougaonik(10.5, 12.4);
Pravougaonici[1] = new Pravougaonik(21.3, 7.2);
Pravougaonici[2] = new Pravougaonik(4.5,  22.6);

if (Pravougaonici[0].P > Pravougaonici[1].P)
{
	Console.WriteLine("Prvi pravougaonik ima veću površinu.");
}
else
{
	Console.WriteLine("Drugi pravougaonik ima veću površinu.");
}
									
		                        
	                            	
Pravougaonik[] Pravougaonici = new Pravougaonik[3];
Pravougaonici[0] = new Pravougaonik(10.5, 12.4);
Pravougaonici[1] = new Pravougaonik(21.3, 7.2);
Pravougaonici[2] = new Pravougaonik(4.5,  22.6);

if (Pravougaonici[0].P > Pravougaonici[1].P)
{
	cout << "Prvi pravougaonik ima veću površinu." << endl;
}
else
{
	cout << "Drugi pravougaonik ima veću površinu." << endl;
}
									
		                        
	                            	
Pravougaonik[] Pravougaonici = new Pravougaonik[3];
Pravougaonici[0] = new Pravougaonik(10.5, 12.4);
Pravougaonici[1] = new Pravougaonik(21.3, 7.2);
Pravougaonici[2] = new Pravougaonik(4.5,  22.6);

if (Pravougaonici[0].P > Pravougaonici[1].P) {
	System.out.printf("Prvi pravougaonik ima veću površinu.");
}
else {
	System.out.printf("Drugi pravougaonik ima veću površinu.");
}
									
		                        
	                            	
Pravougaonici = [3];
Pravougaonici.append(Pravougaonik(10.5, 12.4))
Pravougaonici.append(Pravougaonik(21.3, 7.2))
Pravougaonici.append(Pravougaonik(4.5,  22.6))

if Pravougaonici[0].P > Pravougaonici[1].P:
	print("Prvi pravougaonik ima veću površinu.")
else:
	print("Drugi pravougaonik ima veću površinu.")
									
		                        
Slika 39. - Zadatak pronalaženja većeg od prva dva pravougaonika rešen metodom OOP-a.

Za poređenje, pogledajmo kako bismo isti program zapisali bez klasa i objekata:

C#
C++
Java
Python
		                        	

Double P1_a = 10.5, P1_b = 12.4;
Double P2_a = 21.3, P2_b = 7.2;
Double P3_a = 4.5,  P3_b = 22.6;

Double P1_O = 2 * (P1_a + P1_b);
Double P2_O = 2 * (P2_a + P2_b);
Double P3_O = 2 * (P3_a + P3_b);

Double P1_P = P1_a * P1_b;
Double P2_P = P2_a * P2_b;
Double P3_P = P3_a * P3_b;

if (P1_P > P2_P)
{
	Console.WriteLine("Prvi pravougaonik ima veću površinu.");
}
else
{
	Console.WriteLine("Drugi pravougaonik ima veću površinu.");
}
									
	                            
		                        	

Double P1_a = 10.5, P1_b = 12.4;
Double P2_a = 21.3, P2_b = 7.2;
Double P3_a = 4.5,  P3_b = 22.6;

Double P1_O = 2 * (P1_a + P1_b);
Double P2_O = 2 * (P2_a + P2_b);
Double P3_O = 2 * (P3_a + P3_b);

Double P1_P = P1_a * P1_b;
Double P2_P = P2_a * P2_b;
Double P3_P = P3_a * P3_b;

if (P1_P > P2_P)
{
	cout << "Prvi pravougaonik ima veću površinu.") << endl;
}
else
{
	cout << "Drugi pravougaonik ima veću površinu.") << endl;
}
									
	                            
		                        	

double P1_a = 10.5, P1_b = 12.4;
double P2_a = 21.3, P2_b = 7.2;
double P3_a = 4.5,  P3_b = 22.6;

double P1_O = 2 * (P1_a + P1_b);
double P2_O = 2 * (P2_a + P2_b);
double P3_O = 2 * (P3_a + P3_b);

double P1_P = P1_a * P1_b;
double P2_P = P2_a * P2_b;
double P3_P = P3_a * P3_b;

if (P1_P > P2_P) {
	System.out.printf("Prvi pravougaonik ima veću površinu.");
}
else {
	System.out.printf("Drugi pravougaonik ima veću površinu.");
}
									
	                            
		                        	

P1_a = 10.5
P1_b = 12.4

P2_a = 21.3
P2_b = 7.2

P3_a = 4.5
P3_b = 22.6

P1_O = 2 * (P1_a + P1_b)
P2_O = 2 * (P2_a + P2_b)
P3_O = 2 * (P3_a + P3_b)

P1_P = P1_a * P1_b
P2_P = P2_a * P2_b
P3_P = P3_a * P3_b

if P1_P > P2_P:
	print("Prvi pravougaonik ima veću površinu.")
else:
	print("Drugi pravougaonik ima veću površinu.")
									
	                            
Slika 40. - Zadatak pronalaženja većeg od prva dva pravougaonika rešen upotrebom zasebnih promenljivih.

Razlika nije drastična na ovako malom i jednostavnom primeru (a veći i kompleksniji ipak nismo želeli da pišemo, zarad očuvanja preglednosti), ali, nije teško uvideti koliko je programski kod lepši i pregledniji u slučaju da problem rešavamo metodom OOP-a.

Komplikovaniji primeri samo dodatno povećavaju ovu razliku u korist objektno orijentisanog programiranja (ali, setimo se ovde primedbe iz uvodnog pasusa da OOP ne treba koristiti uvek i bez potrebe i da je proceduralno programiranje, u slučaju jednostavnih programa, krajnje dobar i primeren način rešavanja problema).

Jednostvno, kao što u životu možemo od Beograda do Novog Sada putovati biciklom ili automobilom, dok bismo do Australije ipak radije putovali avionom, tako i u programiranju nećemo OOP pristup koristiti "za svaku sitnicu", već onda kada zatreba - a sada znamo i kako.

Autor članka Nikola Vukićević Za web portal www.codeblog.rs
Napomena: Tekstovi, slike, web aplikacije i svi ostali sadržaji na sajtu www.codeblog.rs (osim u slučajevima gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta www.codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog pismenog odobrenja autora.
©2021. Sva prava zadržana.
Viber
početna Početna > Članci > Uvod u objektno orijentisano programiranje

Info & povezani članci

Info

trejler_sat Datum objave: 04.03.2020.

trejler_dokument Jezici: C#
C++
Java
Python

trejler_teg_crveni Težina: 8/10

Povezani članci

Dinamičko programiranje Strukture podataka Postfiksna notacija - kako računari računaju? Binarno stablo pretrage AVL Stablo - Implementacija BFS i DFS - Pronalaženje putanje kroz lavirint Visinski balansirano (AVL) stablo Klase složenosti algoritama - (O notacija) Uvod u relacione baze podataka i SQL Shunting Yard - Implementacija
Preuzmite PDF verziju
Controlling complexity is the essence of computer programming.
Brian Kernigan
codeBlog codeBlog
Projekat posvećen popularizaciji kulture i veštine programiranja među mladim programerima.
Napomena: Tekstovi i slike na sajtu www.codeblog.rs (osim u slučajevima, gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta www.codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog odobrenja autora.
© 2021. Sva prava zadržana.
Facebook - logo
Instagram - logo
LinkedIn - logo
Twitter - logo
E-mail
Naslovna
   •
Uslovi korišćenja
   •
Obaveštenja
   •
FAQ
   •
Kontakt