Softverski obrasci i komponente

Obrasci ponašanja

Prof. dr Igor Dejanović (igord at uns ac rs)

Kreirano 2023-01-16 Mon 18:15, pritisni ESC za mapu, m za meni, Ctrl+Shift+F za pretragu

Sadržaj

1. Obrasci ponašanja

  • Bave se algoritmima i raspodelom odgovornosti između objekata.
  • Ne definišu samo obrazac strukture već i obrazac komunikacije između objekata.

2. Iterator

2.1. Iterator

Omogućava pristup elementima kolekcije sekvencijalno bez otkrivanja konkretne reprezentacije kolekcije.

2.2. Struktura obrasca

Iterator-Abstract.png

2.3. Java implementacija

Iterator obrazac je direktno podržan u javinim standardnim bibliotekama – Interfejsi Iterable<E> i Iterator<E>.

2.4. Java implementacija

Iterator-Java.png

2.5. Java implementacija - While petlja

List<Integer> intList = new ArrayList<Integer>();
Iterator<Integer> i = intList.iterator();
while(i.hasNext()){
    Integer a = i.next();
    System.out.println(a);
}

2.6. Java implementacija - ForEach petalja

Iteratori su podržani direktno u Java programskom jeziku. For-each petlja u javi se može koristiti za bilo koji objekat koji implementira Iterable<E> interfejs ili za java nizove.

List<Integer> intList = new ArrayList<Integer>();
for(Integer i: intList){
    System.out.println(i);
}

3. Command

3.1. Command

  • Enkapsulacija zahteva za obradom u vidu objekta.
  • Omogućava kreiranje redova za obradu (Queues) kao i undo operacije.
  • Poznat i pod nazivima Action i Transaction.

3.2. Struktura obrasca

Command-Abstract.png

3.3. Saradnja učesnika

Command-Sequence.png

3.4. Šta dobijamo?

  • Razdvajamo objekat koji inicira izvršavanje operacija od onoga koji “zna” kako operaciju treba izvršiti.
  • Command objekti su objekti prvog reda. Mogu se tretirati kao i svi drugi objekti.
  • Komande se mogu komponovati (upotrebom Composite obrasca) i formirati složenije komande – makro komande.
  • Nove komande se lako dodaju – nije potrebno izmeniti postojeće klase.

4. Mediator

4.1. Mediator

  • Objekat koji enkapsulira znanje o interakciji grupe objekata.
  • Omogućava slabo sprezanje (loose coupling) objekata tako što objekti ne referenciraju jedni druge direktno.

4.2. Struktura obrasca

Mediator-Abstract.png

4.3. Primer

Mediator-Concrete.png

4.4. Saradnja učesnika

Mediator-Sequence.png

4.5. Šta dobijamo?

  • Složena interakcija objekata je centralizovana – izmena se vrši nasleđivanjem jedne klase.
  • Objekti koji stupaju u interakciju su slabo spregnuti – objekte i medijator možemo menjati nezavisno.
  • Jednostavniji protokol – veze više-na-više zamenjene vezama jedan-na-više koje su lakše za razumevanje i izmenu.
  • Logika interakcije objekata je odvojena od njihovog individualnog ponašanja – inerakcija se lakše analizira.
  • Problem koji može nastati – monolitan i previše kompleksan medijator objekat.

5. Memento

5.1. Memento

  • Bez narušavanja enkapsulacije beleži i eksternalizuje interno stanje objekta tako da se objekat može kasnije vratiti u identično stanje.
  • Poznat i pod nazivom Token.

5.2. Struktura obrasca

Memento-Abstract.png

5.3. Saradnja učesnika

Memento-Sequence.png

5.4. Šta dobijamo?

  • Očuvavamo enkapsulaciju – memento ne otkriva detalje interne implementacije Originator objekta iako se njegovo stanje čuva van njega. Pristup Memento objektu je moguć samo od strane Originator objekta.
  • Pojednostavljen dizajn Originator objekta – klijenti čuvaju stanje objekta kroz Memento objekat.
  • Bolja skalabilnost - čuvanje stanja se prepušta klijentima.
  • Korišćenje Memento objekata može biti problematično ukoliko je stanje Originator-a definisano velikom količinom podataka.
  • U nekim programskim jezicima je teško sprečiti pristup internoj strukturi Memento objekta od strane drugih objekata u sistemu.

6. Observer

6.1. Observer

  • Definiše međuzavisnost objekata tako da kada se stanje jednog objekta promeni svi zavisni objekti se automatski obaveštavaju.
  • Poznat i pod nazivom Publish-Subscribe.

6.2. Struktura obrasca

Observer-Abstract.png

6.3. Saradnja učesnika

Observer-Sequence.png

6.4. Podrška u Javi

  • Interfejs Observer i klasa Observable u paketu java.util.
  • Mehanizam Listener-a u Swing-u prati ovaj obrazac.

6.5. Šta dobijamo?

  • Apstraktno sprezanje Observer i Observable objekata. Observable ne zna konkretne klase Observer objekata već komunicira sa njima putem jednostavnog interfejsa.
  • Broadcast stil komunikacije. Observable javlja da se promena dogodila. Ne mora se navoditi primalac poruke. Svi zainteresovani osluškivači će dobiti notifikaciju o promeni.
  • Ukoliko Observable ne navodi u pozivu poruke šta je promenjeno, Observer-i moraju to sami da ispitaju što može biti “skupo”.

6.6. Napomene

  • Ukoliko Observer posmatra više Observable objekata potrebno je identifikovati objekat koji šalje poruku o promeni – slanje reference na objekat kao parametra.
  • Izmena stanja Observable objekta prilikom obrade notifikacije može dovesti do beskonačne rekurzije.

7. State

7.1. State

Izmena ponašanja objekta prilikom promene njegovog internog stanja. Objekat se ponaša kao da je promenio klasu.

7.2. Struktura obrasca

State-Abstract.png

7.3. Saradnja učesnika

  • Context delegira operacije zavisne od stanja objektu state (ConcreteStateX).
  • Context može proslediti sebe u zahtevu ukoliko stanje treba da mu pristupa.
  • Klijenti koriste isključivo Context, mada mogu, ukoliko je potrebno, postaviti tekuće stanje.
  • Odluku o prelasku u novo stanje može doneti Context ili tekući state objekat.

7.4. Primer

State-Concrete.png

7.5. Šta dobijamo?

  • Lokalizacija koda specifičnog za određeno stanje u jednu klasu - izbegavanje velikih switch/if-else iskaza.
  • Eksplicitan prelazak između stanja.

7.6. Napomene

  • Promena stanja: Context ili ConcreteStateX?
    • Ukoliko menja ConcreteStateX potrebno je da pristupa Context-u preko interfejsa za promenu stanja.
    • Negativno: stanja moraju da znaju jedna za druge.
    • Ukoliko Context menja stanje – kod može biti dosta kompleksan kod većeg broja stanja.
  • Kreiranje i uništavanje State objekata: jednom pri inicijalizaciji ili pri svakoj promeni stanja.
  • Određeni programski jezici podržavaju oblik dinamičkog nasleđivanja – prirodna podrška za State obrazac.

8. Strategy

8.1. Strategy

Definisanje familije algoritama i omogućavanje njihove izmene bez uticaja na klijenta.

8.2. Struktura obrasca

Strategy-Abstract.png

8.3. Primer

Strategy-Concrete.png

8.4. Šta dobijamo?

  • Definisanje familije algoritama. Nasleđivanje strategija se može koristiti za implementaciju zajedničke funkcionalnosti algoritama.
  • Izmena ponašanja objekta dinamički. Nasleđivanjem kontekst klase može se postići izmena funkcionalnosti ali je ona statičke prirode.
  • Eliminacija iskaza uslova (if-else, switch).
  • Omogućavamo klijentu izbor algoritma i njegovih performansi (npr. manji utrošak memorije ili brže izvršavanje).

9. Template Method

9.1. Template Method

  • Definisanje strukture algoritma pri čemu se definisanje određenih koraka ostavlja klasama naslednicama.
  • Struktura algoritma je nepromenjiva ali se određeni koraci mogu promeniti.

9.2. Struktura obrasca

TemplateMethod-Abstract.png

9.3. Primer

TemplateMethod-Concrete.png

9.4. Šta dobijamo?

  • Mogućnost definisanja invarijantnih delova algoritma u apstraktnoj klasi a varijabilnih delova u podklasama – sprečava se dupliranje koda.
  • Invertovanje kontrole – Hollywood principle

    Don’t call us, we’ll call you

  • Definisanje tzv. hook operacija. Najčešće su prazne ali mogu imati definisano i standardno ponašanje. Za razliku od apstraktnih operacija, hook operacije su opcione (ne moraju se redefinisati).

10. Visitor

10.1. Visitor

  • Razdvajanje algoritma od strukture podataka nad kojim operiše.
  • Lokalizacija implementacije operacije koja se izvršava nad elementima složene strukture podataka (npr. čvorovima stabla).

10.2. Struktura

Visitor-Abstract.png

10.3. Saradnja učesnika

Visitor-Sequence.png

10.4. Primer

Visitor-Concrete.png

10.5. Kada koristiti?

  • Objektna struktura sadrži elemente različitih klasa a potrebno je izvršiti operaciju koja zavisi od konkretne klase objekta.
  • Više različitih, nesrodnih operacija je potrebno izvršiti nad elementima složene strukture a želimo izbeći “zagađivanje”, svih klasa čiji objekti čine složenu strukturu, sa novom metodom za svaku operaciju.
  • Hijerarhija klasa objektne strukture se retko menja dok dodavanje novih operacija nad strukturom može biti česta operacija.

10.6. Napomene

  • Visitor predstavlja implementaciju Double Dispatch mehanizma. Pozvana operacija zavisi istovremeno od tipa konkretnog Visitor objekta kao i od tipa konkretnog elementa strukture nad kojom se operacija vrši.
  • Srodne operacije su lokalizovane u jednoj klasi.

11. Chain of Responsibility

11.1. Chain of Responsibility

Izbegavanje jakog sprezanja objekta koji šalje zahtev od objekta koji vrši obradu zahteva tako što se objekti koji vrše obradu uvezuju u listu i daje se šansa svakom u nizu da obradi događaj sve dok neko ne odgovori pozitivno na zahtev (izvrši obradu).

11.2. Struktura

ChainOfResponsibility-Abstract.png

11.3. Primer

ChainOfResponsibility-Concrete.png

11.4. Kada koristiti?

  • Više od jednog objekta može da obradi zahtev a obrađivač nije poznat unapred.
  • Želimo da uputimo zahtev jednom iz grupe objekata a da ne navodimo konkretnog obrađivača eksplicitno.
  • Lista mogućih obrađivača može dinamički da se menja u vreme izvršavanja.

11.5. Napomene

  • Dve vrste: prosleđivanje zahteva niz lanac i posle obrade ili prekid prosleđivanja.
  • Objekat koji vrši obradu nije eksplicitno definisan pa ne postoji garancija da će zahtev biti obrađen.

12. Literatura

  • E. Gamma, R. Helm, R. Johnson, and J. M. Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley Professional, 1994
  • M. Grand, Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML, John Wiley & Sons, Inc., vol. 1, 2002