TÉMATICKÝ OKRUH Softwarové inženýrství Číslo otázky : 29. Otázka : Zpracování událostí: mechanismus událostí a jejich zpracování (Event/Listener), nepřímá invokace (Observer/Observable). Obsah : 1. Mechanisums událostí a jejich zpracování(event/listener) 2. Observer/Observable
1. Mechanismus událostí a jejich zpracování(event/listener) Mechanismus událostí slouží k interakci programus uživatelem a operačním systémem. Událost nastává například při stisku tlačítka, pohybu myši, změně pozice v seznamu pokusu o zavření okna a podobně. Události Události tvoří dva prvky : zdroj události posluchač Zdroj události Je objekt, který generuje události a spravuje seznam registrovaných posluchačů. Posluchač Objekt, který chce být o události informován, musí být registrován u zdroje událostí a musí implementovat dohodnuté rozhraní. Postup (životní cyklus události): 1. Událost vznikne (typicky uživatelskou akcí nad komponentou GUI). 2. Na komponentu musí být "zavešen" posluchač dané události, to je posluchač je registrován u zdroje události 3. Zdroj události (tlačítko) projde seznam registrovaných posluchačů a každému z nich oznámí vznik události: - zavolá dohodnutou metodu rozhraní posluchače - metodě předá informace o události (podtřída java.util.eventobject) Události mohou souviset s uživatelskou akcí nad/s: oknem WindowEvent klávesnicí KeyEvent myší (klikání a pohyb) MouseEvent získáním nebo ztrátou fokusu FocusEvent (obecnou) akcí nad GUI (stisk tlacítka v GUI) - ActionEvent K tomu, aby daná komponenta byla schopna provádět funkce a reagovat na naše podněty (kliknutí myší) používá Java systém událostí (events), které komponenta generuje a tzv. posluchačů (listeners), kteří slouží pro zachycení a následné zpracování vygenerované události. Posluchači tedy představují speciální objekty, jejichž jediným účelem je zachycení události a zpracování odezvy na tuto událost. Tedy jak jsme jiz výše zmiňovali : 1. Nejprve pro komponentu zaregistrujeme jejího posluchače (obvykle metodou addsomelistener()) pro zpracování událostí, které bude při své činnosti generovat. 2. Pokud za běhu programu komponenta vygeneruje událost (například po kliku myší) a má pro tento typ události zaregistrován posluchač, následuje zpracování této události metodami posluchače.
2. Nepřímá invokace Observer/Observable Jedná se o návrhový vzor Pozorovatel, který je velmi často používána v různých konkrétních situacích. Nasazuje se v situacích, kde objekt, který mění své chování a jeho pozorovatelé neudržují navzájem žádné vazby a jejich propojení je zprostředkována jejich supertřídami. Smysl této nepřímé invokace spočívá v tom, že o přidání nového pozorovatele není nutné informovat (a tím měnit i zdrojový kód) pozorovaný objekt, ale stačí pouze vytvořit novou třídu, která je specializací třídy Pozorovatel a její instance přidat do množiny pozorujících objektů prostřednictvím operace připoj. Tento návrhový vzor tvoří základ všech moderních grafických uživatelských rozhraní, kde změna stavu pozorovaných objektů vede k událostem (obvykle objekt předávaný jako parametr operace oznam), které jsou zpracovávány pozorujícími objekty pomocí operací typu aktualizuj. Využití tohoto vzoru je především v návrhu a implementaci informování jednotlivých komponent o změnách ve zbývajících částech systému. Existuje několik otázek, které musí být brány v úvahu při využití tohoto vzoru: Jestliže frekvence změn v pozorovaném objektu je příliš velká, mohlo by informování o každé této změně vést k přílišnému zatížení systému. V takovém případě je vhodné uvažovat o informování pozorovatelů až po určité sadě změn. V návrhovém vzoru není uvažována situace, kdy jeden pozorovatel je závislý na více objektech. V takovém případě musí být uvažováno o správné implementaci metody aktualizuj nebo o definici více takových metod. Použitím více modifikací této metody, lze zajistit i předávání různých druhů událostí. Měla by být také ošetřena situace, kdy Subject končí svoji činnost. Před vlastním uvolněním objektu Subject by mělo dojít k informování pozorovatelů. Musí být řízena vzájemná závislost jednotlivých pozorovaných objektů. Pozorovaný objekt neví nic o implementaci metody aktualizuj u pozorovatelů, a proto se ani nezajímá o dopad volání této metody na systém. Jestliže informace o změně a následná synchronizace dat, vyvolá nekontrolovaný kaskádový efekt celým systémem (pozorovatel působí i jako pozorovaný objekt), mohou se vyskytnout výkonnostní i logické problémy.
Problémem v implementaci může nastat, jestliže je vyvolána výjimka při informování pozorovatele (metoda aktualizuj). Obecně je předpoklad, že pozorovatel nevyvolává výjimky. Není zde nikdo, kdo by je odchytil a zpracoval, protože Subject nezná implementaci pozorovatele, a proto se nestará ani o případné výjimky. Řešením této situace může být implementace dvou fázového protokolu komunikace. V první fázi Subject deklaruje, že se chystá změnit data. Pozorovatelé mohou tuto změnu odmítnout nebo potvrdit. Jestliže je udělen souhlas od všech pozorovatelů, je změna provedena a probíhá klasická komunikace dle návrhového vzoru Známá použití - MVC ( Model/View/Controller ) - třída Model odpovídá subjektu, View pozorovateli - InterViews - Observer a Observable - Andrew Toolkit - "view a "data object" - Unidraw - rozdělení objektů grafických editorů na části View a Subject - MFC - architektura Document/View - java.util.observable implementace pro použití v JDK - Java Swing - používá vzor Observer pro event management - Boost.Signals, Qt, libsigc++, sigslot - signal/slot model Příklad použití Motivace: Uvažujme příklad z obr. 1. Objekt třídy Faktura implementuje operaci zaplacena takovým způsobem, že v okamžiku, kdy je tato zpráva obdržena objekt informuje manažera a prodejce zasláním zprávy odešli na instance třídy SMSBrána a stejně tak zasílá zprávu zobraz na instanci třídy MonitorPlateb. Nevýhodou je, že každá faktura musí vědět o těchto svých pozorovatelích, pamatovat si je a zaslat jim odpovídající zprávy. Zavedení dalšího nového pozorovatele by také znamenalo upravit zdrojový kód Faktury. Obr.1 Motivační příklad návrhového vzoru Pozorovatel Řešení: Problém stanovení n pozorovatelů na jediném pozorovaném lze řešit pomocí návrhového vzoru Pozorovatel tím způsobem, že zavedeme dvě nové třídy Předmět pozorování a Pozorovatel (obr. 2). První z nich zavádí operace přidání a odebrání instancí třídy Pozorovatel a operaci oznam, která v případě svého provedení odešle na všechny zaregistrované pozorovatele zprávu aktualizuj. Třída Pozorovatel tedy zavádí operaci aktualizuj, která je jednotlivými podtřídami předefinována tak, aby odpovídajícím způsobem dokázaly jednolivé instance správně reagovat instance třídy SMSBrána odešle textovou
zprávu, zatímco instance třídy MonitorPlateb zobrazí informaci o zaplacení faktury na obrazovku. Operace oznam je vyvolána podtřídou třídy Předmět, v našem prípadě se jedná o výše zmíněnou třídu Faktura. Obr. 2: Řešení s použitím návrhového vzoru Pozorovatel