Posmatrani i objekti posmatraci Nekada je potrebno da jedan objekat odreaguje na promene drugog. Npr. kada se promeni centar pravougaonika, treba da se promeni i centar njegovog opisanog kruga, dok promena duzina stranica pravougaonika treba da utice na promenu duzine poluprecnika njegovog opisanog kruga. Ako smo prilikomkreiranja opisanog kruga zvali konstruktor koji pravi kopiju centralne tacke (a mi na vezbama jesmo), nijedna od ovih promena nece se desavati automatski. Da je konstruktor samo uzimao referencu na postojecu centralnu tacku, promena centra pravougaonika dovodila bi do promene centra njegovog opisanog kruga. Medjutim, promena duzina stranica pravougaonika ni na koji nacin ne bi mogla automatski da dovede do promene duzine poluprecnika opisanog kruga. Ako se neka klasa izvede iz klase Observable (koja se nalazi u paketu java.util), objekat te klase moci ce da se posmatra. Preciznije, moci ce da mu se pridruze objekti koji ce ga posmatrati i automatski reagovati kada ih posmatrani objekat obavesti da su se desile promene za koje su posmatraci zainteresovani. Mehanizam posmatranja funkcionise na sledeci nacin: Objekat koji se posmatra mora biti objekat klase izvedene iz Observable, dok objekat posmatrac mora pripadati klasi koja implementira interfejs Observer (takodje iz paketa java.util). Pridruzivanje objekta posmatraca objektu koji se posmatra vrsi se pomocu metoda addobserver() (videti o njemu u delu Metodi klase Observable ). Specijalno, jedna ista klasa moze istovremeno biti izvedena iz Observable i implementirati interfejs Observer, tako da objekat posmatrac
moze biti istog tipa kao i objekat koji posmatra. Posmatrac je zainteresovan da odreaguje kada se dese izvesne promene nad objektom koji posmatra. Posmatrani objekat, nakon sto se te promene dese, najpre treba da pozove metod setchanged(), a potom i da obavesti svoje posmatrace da se promena desila, sto cini pozivom neke od 2 verzije metoda notifyobservers(). Jedna verzija ne prima nikakve argumente, dok druga prima jedan argument tipa Object. Koriscenjem druge verzije metoda, uz obavestenje o promeni, moguce je proslediti objekat proizvoljnog tipa koji moze sadrzati dodatne informacije o promeni. U klasi posmatraca, koja mora implementirati interfejs Observer, neophodno je definisati (jedini) metod ovog interfejsa: public void update(observable theobservableobject, Object arg) { // This method is called when the observed object changes } koji ce biti automatski pozivan kada se desi promena nad odgovarajucim posmatranim objektom. Prvi argument je referenca na objekat koji se promenio i uzrokovao poziv ovog metoda. Time se omogucuje objektu posmatracu da pristupa public metodima objekta koji posmatra. Drugi argument se koristi kako bi se dostavile dodatne informacije posmatracu (odgovara argumentu metoda notifyobservers(), kada se koristi verzija sa argumentom). Metodi klase Observable: addobserver(observer o) deleteobserver(observer o) deleteobservers() notifyobservers(object arg) dodavanje posmatraca uklanjanje posmatraca uklanjanje svih posmatraca poziva metod update() svih posmatraca, ako je objekat promenjen
notifyobservers() countobservers() setchanged() haschanged() clearchanged() (tj. pozvan je metod setchanged()). Tekuci objekat i argument arg prosledjen ovom metodu prosledjuju se metodu update()svakog posmatraca. Ekvivalentno sa: notifyobservers(null) broj objekata posmatraca mora se pozivati pre notifyobservers() vraca true akko je objekat promenjen resetuje status objekta u nepromenjen Primer 1 (Povrs, Pravougaonik, Kvadrat, Krug) Zelimo da se opisani krug oko pravougaonika automatski azurira prilikom promene centralne tacke ili duzina stranica pravougaonika. Bazna klasa Povrs izvedena je iz klase Observable, dodat je metod setcentar() koji omogucuje promenu centralne tacke povrsi (specijalno i pravougaonika) i u ovom metodu se, nakon promene centralne tacke, pozivaju metodi setchanged() i notifyobservers(). U klasi Pravougaonik dodati su metodi seta() i setb() za promenu duzina stranica. U svakom od njih se, takodje nakon odgovarajuce promene, pozivaju metodi setchanged() i notifyobservers(). U metodima seta() i setb() treba povesti racuna da se u slucaju kvadrata promenom ponovo dobije kvadrat (ako je u pitanju instanca klase Kvadrat i druga stranica se postavi na novu duzinu). U metodu opisanikrug(), krug koji se kreira postaje objekat posmatrac za svoj pravougaonik
(poziv metoda addobserver()), tako da ce biti obavesten svaki put kada se za pravougaonik pozove setcentar(), seta() ili setb(). Klasa Krug objekata posmatraca, implementira interfejs Observer. U definiciji metoda update() interfejsa, krug se azurira tako da mu centar bude centar pravougaonika oko koga je opisan (a on je njegov posmatrani objekat), a poluprecnik jednak polovini duzine dijagonale tog istog pravougaonika. Tom prilikom neophodno je vrsiti kastovanje u stvarni tip posmatranog objekta (Pravougaonik). U test-klasi kreiraju se i ispisuju pravougaonik i odgovarajuci opisani krug, nakon cega se menja centar pravougaonika, a potom duzina stranice a, pa duzina stranice b. Nakon svake promene ispisuju se String-reprezentacije pravougaonika i opisanog kruga. Metod update() se automatski poziva za objekat posmatrac krug, tako da on na odgovarajuci nacin prati promene svog pravougaonika. Na kraju, kreira se kvadrat, njegov opisani krug, a zatim se poziva metod setb() da promeni duzinu stranice b. Biva promenjena i duzina stranice a, tako da je rezultujuca povrs i dalje kvadrat, a zbog automatskog poziva metoda update() i opisani krug je onakav kakav bi trebalo da bude. Primer 2 (Stigla plata) U ovom primeru imamo bankovni racun, za koji je obavezno poznato stanje na racunu, a mozda i mobilni vlasnika racuna, u kom slucaju se vlasniku salje SMS prilikom svake uplate. Dakle, bankovni racun je posmatrani objekat, a njegov posmatrac je mobilni racun. Kada uplata legne na bankovni racun, pozivaju se metodi setchanged() i
notifyobservers(,) a kao dodatna informacija salje se kolicina uplacenog novca (tipa int). Klasa RacunMobilni implementira interfejs Observer jer su njeni objekti posmatraci objekata klase RacunBanka. U metodu update() poziva se metod stigaosms() koji ce ispisati informacije o tekucem stanju na bankovnom racunu (posmatrani objekat, o, ciji je stvarni tip RacunBanka) i kolicini uplacenog novca (drugi argument, arg, odgovara argumentu metoda notifyobservers(), a njemu je prosledjen argument tipa int). S druge strane, u slucaju da je osoba koja je vlasnik mobilnog (a i odgovarajuceg bankovnog racuna) majka, odnosno otac deteta, dete ce reagovati na dolazak SMS-a. U tom scenariju, mobilni je posmatrani objekat, dok je dete njegov posmatrac. Kako objekti klase MobilniRacun mogu da se posmatraju, klasa je izvedena iz Observable, a u metodu stigaosms() pozivaju se metodi setchanged(), a potom i notifyobservers(), kako bi dete bilo obavesteno da se desilo ono sto ga zanima Klasa Dete implementira interfejs Observer, jer dete posmatra mobilni. U metodu update(), u zavisnosti od pola vlasnika mobilnog, dete ce mu se obratiti sa Mama, odnosno Tata, a nakon toga izreci slucajnu od recenica iz statickog niza stringova repertoar. Klasa nije izvedena iz Observable jer, u ovom slucaju, dete niko ne posmatra i ne reaguje ni na kakve promene! U test-klasi kreiramo jednu osobu zenskog pola, sa brojem telefona 064123456789, na cijem bankovnom racunu se inicijalno nalazi 200 dinara, i jednu osobu muskog pola, sa brojem telefona 064987654321, na
cijem bankovnom racunu se, takodje, inicjalno nalazi 200 dinara. To su ponosni roditelji Milice i Perice, koji pomno prate da odreaguju kada roditeljima stigne SMS (iz banke). 5 puta roditeljima stize plata. Pre stizanja plate, sa verovatnocom 30% promeni im se broj telefona (ne i operater), a plata im je slucajan od brojeva 100, 200, 300,, 1000. Prilikom svake uplate, na mobilni stize SMS, a na njega reaguju i Perica i Milica