Observer (návrhový vzor)
Author
Albert FloresObserver (pozorovatel, vydavatel) je návrhový vzor.
Terminologie
Vydavatel (pozorovaný), v anglickém originále "subject". * Předplatitel (posluchač), v anglickém originále "observer".
Mezi předplatiteli a vydavateli zavádí vztah N:1 (jeden vydavatel má mnoho předplatitelů). V případě, že na straně vydavatele dojde ke změně vztahu či výskytu nějaké události, upozorní na to automaticky všechny své předplatitele. +more Vydavatel zná své předplatitele a může jich mít neomezené množství. Vydavatel má také veřejné rozhraní, skrze které se předplatitelé mohou přihlašovat nebo odhlašovat. Předplatitel má veřejné rozhraní, které má být použito k jeho upozornění na změnu na straně vydavatele. Toto veřejné rozhraní předá vydavateli při přihlašování. Alternativně může předplatitel vydavateli předat sám sebe jako parametr.
Při rozdělování systému na soubor spolupracujících tříd je třeba zachovat konzistenci mezi souvisejícími objekty. Řešení konzistence prostřednictvím těsného svázání (v angl. +more originále tight coupling) objektů není žádoucí, protože snižuje znovupoužitelnost objektů.
Použití
Návrhový vzor je vhodný pro následující případy: * Abstrakce má dva parametry, které jsou na sobě navzájem závislé. Zapouzdřením parametrů je dosaženo větší obměnitelnosti a znovupoužitelnosti. +more * Změna jednoho objektu vyžaduje změnu dalších objektů a není známo, kolik objektů je třeba změnit. * Objekt má být schopen upozornit další objekty, aniž by o těchto objektech věděl detaily.
Příklad
Příkladem použití může být tabulkový editor (např. MS Excel nebo OpenOffice Spreadsheet), který odděluje aplikační data od prezentační vrstvy. +more Zdrojová data mohou být uživateli prezentována několika způsoby současně, například jako tabulka i jako graf. V případě, že dojde ke změně zdrojových dat, je třeba, aby všechny prezentující objekty, které daná zdrojová data používají, byly na tuto změnu upozorněny.
Strategie
Při upozorňování předplatitelů na změny na straně vydavatele je možné implementovat dvě různé strategie, podle toho, na čí straně je iniciativa. Nicméně v obou případech jde o netriviální a opakující se komunikační zátěž.
Tažná strategie
U tažné strategie vydavatel informuje předplatitele o změně svého stavu. Předplatitel si následně vyžádá od vydavatele parametry, které ho zajímají. +more Nevýhodou této strategie je, že předplatitel musí mít funkci, která bude vydavatele žádat o parametry, a to periodicky (polling). Strategii je vhodné použít v případech, kdy náklady vynaložené na následnou komunikaci jsou menší než náklady, které by byly vynaložené pro rozeslání všech parametrů všem předplatitelům.
Tlačná strategie
U tlačné strategie vydavatel zároveň s informací o změně svého stavu předá předplatitelům všechny své parametry: broadcast. Předplatitel si vybere pouze ty informace, které ho zajímají (pokud vůbec nějaké). +more Tím je cílená vzájemná komunikace omezena na minimum. Tuto strategii je vhodné použít v případech, kdy předplatitele zajímají všechny, nebo většina parametrů, které rozesílá vydavatel. Nebo v případech, kdy by objem následné komunikace (předplatitel si vyžádá parametry, vydavatel je zasílá) přesáhl celkový objem komunikace, kdy vydavatel posílá rovnou všechny své parametry sám od sebe, bez určování dílčí položky.
Výhody
Abstraktní spojení mezi vydavatelem a předplatitelem. Každý vydavatel zná seznam svých předplatitelů, z nichž každý implementuje rozhraní abstraktní třídy Observer. +more Díky tomu je spojení mezi vydavatelem a předplatitelem volné (vydavatele nezajímá, jaké třídy je předplatitel).
Podpora všesměrového vysílání: broadcast. Na rozdíl od běžné komunikace, kdy objekty přesně určí adresáta své zprávy, návrhový vzor Observer podporuje všesměrovou komunikaci. +more Vydavatel neadresuje konkrétně každého svého předplatitele, ale automaticky posílá zprávu všem předplatitelům, kteří jsou k němu přihlášeni.
Nevýhody
Nečekané aktualizace. Vzhledem k tomu, že vydavatelé o sobě navzájem nevědí, může nastat situace, kdy změna stavu u jednoho objektu vyvolá vlnu aktualizací, která se kaskádovitě šíří (vydavatel může být zároveň předplatitelem jiného vydavatele). +more Navíc pokud nejsou správně definována kritéria závislostí, lze jen těžko vystopovat podvržené aktualizace. Samotný aktualizační protokol neposkytuje žádné info o zdroji, který změny vyvolal, což vystopování zdroje aktualizace ještě více ztěžuje.
Implementace
V některých případech má smysl, aby byl předplatitel přihlášen k více vydavatelům zároveň. V takovém případě je třeba rozšířit rozhraní pro aktualizace (a s ním související aktualizační protokol) tak, aby byl znám i zdroj aktualizací. +more Tím je zajištěna konzistence se správnými vydavateli (integrita dat).
Pokud je třeba mazat vydavatele, je třeba mazání ošetřit tak, aby se předplatitelé neodkazovali na již neexistující objekty vydavatelů. Například tak, že při mazání vydavatel oznámí svým předplatitelům, že bude smazán. +more Předplatitelé tak mohou aktualizovat své odkazy.
Je třeba zajistit aby stav vydavatele byl, ve chvíli kdy upozorňuje své předplatitele na změnu, neměnný. Pokud by se jeho stav měnil i poté, co upozornil své předplatitele, mohlo by dojít k situaci kdy různí předplatitelé dostanou rozdílné informace o stavu vydavatele (v závislosti na pořadí a času, kdy si vyžádali jeho parametry).
V některých případech je vhodné rozšířit přihlašovací rozhraní i o předmět zájmu. Předplatitel při této akci specifikuje, které parametry ho zajímají. +more Vydavatel pak upozorňuje pouze ty předplatitele, které zajímá právě změněný parametr.
Zapouzdření komplexní sémantiky aktualizací. Pokud je na vydavateli závislé velké množství předplatitelů a zároveň vydavatelé vyžadují oznámení o změně v jiném formátu, je vhodné použít SprávceZměn (v angl. +more originále ChangeManager). Tento přijme aktualizaci od vydavatele a předtím, než jí zprostředkuje předplatitelům, jí upraví tak, aby jí předplatitelé rozuměli. SprávceZměn má tři úkoly. Shromažďuje požadavky předplatitelů na formát aktualizace a poskytuje rozhraní, které toto shromažďování umožňuje. Definuje aktualizační strategie (tažná vs. tlačná). Posledním úkolem je, že na žádost vydavatele aktualizuje všechny závislé předplatitele.
Kombinace tříd vydavatele a předplatitele. Programovací jazyky, které nepodporují vícenásobnou dědičnost, obecně definují pouze jednu třídu, která kombinuje rozhraní obou objektů (předplatitele i vydavatele). +more Příkladem takového programovacího jazyka je Smalltalk.
Příklady použití
První a zřejmě nejznámější implementace návrhového vzoru Observer je v programovacím jazyce Smalltalk jako Model - View - Controller (MVC). Model je v tomto případě sdílenou informací, Controller (řadič) je vydavatelem, který šíří aktualizaci modelu, a View (Pohled) je předplatitelem.
Příklad v programovacím jazyku Java
Soubor myapp. java obsahuje metodu main, která je určena pro spuštění kódu. +moreWikipedia: Observer (pattern). 29 November 2009 [cit. 7. 12. 2009]. EN. Dostupný z WWW: . /* File Name : EventSource. java */ package obs; import java. util. Observable; //Observable is here import java. io. BufferedReader; import java. io. IOException; import java. io. InputStreamReader;.
public class EventSource extends Observable implements Runnable { public void run { try { final InputStreamReader isr = new InputStreamReader( System. in ); final BufferedReader br = new BufferedReader( isr ); while( true ) { String response = br. +morereadLine; setChanged; notifyObservers( response ); } } catch (IOException e) { e. printStackTrace; } } } /* File Name: ResponseHandler. java */.
package obs;
import java.util.Observable; import java.util.Observer; /* this is Event Handler */
public class ResponseHandler implements Observer { private String resp; public void update (Observable obj, Object arg) { if (arg instanceof String) { resp = (String) arg; System. out. +moreprintln("\nReceived Response: "+ resp ); } } } /* Filename : myapp. java */ /* This is the main program */.
package obs;
public class MyApp { public static void main(String args[]) { System.out.println("Enter Text >");
// create an event source - reads from stdin final EventSource evSrc = new EventSource;
// create an observer final ResponseHandler respHandler = new ResponseHandler;
// subscribe the observer to the event source evSrc.addObserver( respHandler );
// starts the event thread Thread thread = new Thread(evSrc); thread.start; } }
Související návrhové vzory
Mediator (Prostředník) - SprávceZměn působí jako prostředník mezi vydavatelem a předplatiteli. * Singleton (Jedináček) - Na SprávceZměn je použit návrhový vzor Jedináček, tím je zabezpečeno, že všechny aktualizace bude obsluhovat jediný a globálně dostupný SprávceZměn. +moreIBM: Design pattern projects - example. 27 August 2001 [cit. 7. 12. 2009]. EN. Dostupný z WWW: .