Vaše jistota na trhu IT Návrhové vzory Rudolf PECINOVSKÝ [email protected] Stručný obsah ► 00: Předehra ► 01: První kontakt ► 02: Vzory řešící počet instanci ► 03: Skrývání implementace ► 04: Optimalizace rozhraní ► 05: Otevřenost variantním řešením ► 06: Ochrana před rozrůstáním ► 07: Dodatečné úpravy Copyright © 2009, Rudolf Pecinovský ICZ 2 Podrobný obsah ► 00: Předehra ● Ohlédnutí do historie ● Quo vadis programování? ● Základní principy OOP ● Rozhraní ● Zapouzdření a skrývání implementace ● Další užitečné zásady ► 01: První kontakt ● Jednoduchá tovární metoda (Simple factory method) ● Neměnné objekty (Immutable objects) ● Přepravka (Crate) ● Služebník (Servant) ● Prázdný objekt (Null Object) Copyright © 2009, Rudolf Pecinovský 1/2 001 002 003 004 005 006 011 012 013 014 015 ICZ ► 02: Vzory řešící počet instanci ● Společné vlastnosti 020 ● Knihovní třída (Library class, Utility) ● Jedináček (Singleton) ● Výčtový typ (Enumeration) ● Fond (Pool) ● Originál (Original) ● Muší váha (Flyweight) ► 03: Skrývání implementace ● Zástupce (Proxy) ● Příkaz (Command) ● Iterátor (Iterator) ● Stav (State) ● Šablonová metoda (Template Method) 021 022 023 025 024 026 031 032 033 034 035 3 Podrobný obsah ► 04: Optimalizace rozhraní ● Fasáda (Facade) ● Adaptér (Adapter) ● Strom (Composite) ► 05: Otevřenost variantním řešením ● Tovární metoda (Factory method) ● Prototyp (Prototype) ● Stavitel (Builder) ● Abstraktní továrna (Abstract factory) Copyright © 2009, Rudolf Pecinovský 1/2 041 042 043 051 052 053 054 ICZ ► 06: Ochrana před rozrůstáním ● Dekorátor (Decorator) 061 ● Řetěz odpovědnosti (Chain of responsibility) ● Pozorovatel (Observer) ● Prostředník (Mediator) ► 07: Dodatečné úpravy ● Most (Bridge) ● Strategie (Strategy) ● MVC (Model – View – Controller) ● Návštěvník (Visitor) ● Pamětník (Memento) ● Interpret (Interpreter) 062 063 064 071 072 073 074 075 076 4 Vaše jistota na trhu IT Předehra 00 00 ► Ohlédnutí do historie 001 ► Quo vadis programování? 002 ► Základní principy OOP 003 ► Rozhraní 004 ► Zapouzdření a skrývání implementace 005 ► Další užitečné zásady 006 Vaše jistota na trhu IT První kontakt 01 01 ► Jednoduchá tovární metoda (Simple factory method) 011 ► Neměnné objekty (Immutable objects) 012 ► Přepravka (Crate, Transport Object) 013 ► Služebník (Servant) 014 ► Prázdný objekt (Null Object) 015 Návrhové vzory – Design Patterns ► Programátorský ekvivalent matematických vzorečků ax2 bx c 0 ► Výhody: b b 2 4ac x1, 2 2a ● Zrychlují návrh (řešení se nevymýšlí, ale jenom použije) ● Zkvalitňují návrh ● Jsou ověřené, takže výrazně snižují pravděpodobnost potenciálních chyb typu na něco jsme zapomněli ● Zjednodušují a zpřesňují komunikaci mezi členy týmu (větou, že diskriminant je záporný, řeknu znalým jednoduše řadu věcí, které bych musel jinak složitě vysvětlovat) ► Znalost návrhových vzorů patří k povinné výbavě současného objektově orientovaného programátora Copyright © 2009, Rudolf Pecinovský ICZ 7 Vaše jistota na trhu IT Obsah Jednoduchá tovární metoda 010 ► Jednoduchá (někdo používá termín statická) tovární metoda je zjednodušenou verzí návrhového vzoru Tovární metoda. Definuje statickou metodu nahrazující konstruktor. Používá se všude tam, kde potřebujeme získat odkaz na objekt, ale přímé použití konstruktoru není z nejrůznějších příčin optimálním řešením. Charakteristika ► Speciální případ obecné tovární metody ► Náhražka konstruktoru v situacích, kdy potřebujeme funkčnost nedosažitelnou prostřednictvím konstruktorů ► Kdy po ni sáhneme ● Potřebujeme rozhodnout, zda se opravdu vytvoří nová instance ● Potřebujeme vracet instance různých typů ● Potřebujeme provést nějakou akci ještě před tím, než se zavolá rodičovský konstruktor ● Potřebujeme více verzí se stejnými sadami parametrů (tovární metody mohou mít různé názvy) Copyright © 2009, Rudolf Pecinovský ICZ 9 Implementace ► Statická metoda vracející instanci daného typu ► Nemusí vracet vlastní instanci, ale může si vybrat potomka, jehož instanci následně vrátí ► Jmenné konvence ● getInstance ● getXXX, kde XXX je název vraceného typu ● valueOf ● Naznačující účel probablePrime Copyright © 2009, Rudolf Pecinovský ICZ 10 Příklad: AČlověk ► Tovární metoda může rozhodnout o skutečném typu vráceného objektu public static AČlověk getČlověk() { switch ( index++ % 3 ) { case 0: return new Lenoch(); case 1: return new Čilouš(); case 2: return new Pracant(); default: throw new RuntimeException( "Špatně definované maximum" ); } } Copyright © 2009, Rudolf Pecinovský ICZ 11 Příklad ze standardní knihovny – Integer private static class IntegerCache { private IntegerCache(){} Pomocná vnořená třída; zásobník instancí static final Integer cache[] = new Integer[-(-128) + 127 + 1]; static { for (int i = 0; i < cache.length; i++) cache[i] = new Integer(i - 128); } } public static Integer valueOf(int i) { final int offset = 128; if (i >= -128 && i <= 127) { // must cache return IntegerCache.cache[i + offset]; } return new Integer(i); Tovární metoda } pro získání instance Copyright © 2009, Rudolf Pecinovský ICZ 12 Vaše jistota na trhu IT Obsah Neměnné objekty (Immutable objects ) ► Neměnný objekt je hodnotový objekt, u nějž není možno změnit jeho hodnotu. 012 Primitivní a objektové typy Java pro zvýšení efektivnosti dělí datové typy na: ► Primitivní někdo je označuje jako hodnotové, protože se u nich pracuje přímo s hodnotou daného objektu (číslo, znak, logická hodnota) ► Objektové někdo je označuje za referenční, protože se u nich pracuje pouze s odkazem („referencí“) na objekt ● Označování odkazových typů jako referenční je z jazykového hlediska stejná chyba, jako překládat control (řídit) kontrolovat (check) ● Reference (česky) = zpráva, dobrozdání, doporučení, posudek ● Viz Akademický slovník cizích slov, ISBN 80-200-0524-2, str. 652 ● Reference (anglicky) = zmínka, narážka, odkaz, vztah, … ● Viz Velký anglicko-český slovník, nakl. ČSAV 21-055-85, str. 1181 ► Dělení na hodnotové a odkazové není terminologicky vhodné protože objektové typy dále dělíme na hodnotové a odkazové Copyright © 2009, Rudolf Pecinovský ICZ 14 Dělení objektových datových typů ► Odkazové objektové datové typy (reference object data types) ● Neuvažujeme o hodnotách, objekt představuje sám sebe, nemá smysl hovořit o ekvivalenci dvou různých objektů ● Příklady ● Geometrické tvary ● Vlákna ► Hodnotové objektové datové typy (value object data types) ● Objekt zastupuje nějakou hodnotu ● Objekty se stejnou hodnotou se mohou vzájemně zastoupit => má smysl hovořit o jejich ekvivalenci ● Příklady ● Obalové typy, zlomky, velká čísla a další matematické objekty ● Barvy, časy, data ● Překrývají metody equals(Object) a hashCode() ● Tyto metody je vždy vhodné překrýt obě, jinak hrozí problémy Copyright © 2009, Rudolf Pecinovský ICZ 15 Terminologický guláš na platformě .NET ► Platforma .NET používá vlastní terminologie – jako hodnotové typy označují datové typy, u nichž je přiřazení realizováno kopírováním obsahu paměti přiřazené objektu ● Takovéto datové typy budeme označovat jako kopírované ► „Hodnotový“ datový typ v terminologii .NET vůbec nemusí reprezentovat nějakou hodnotu ► Kopírované objektové datové typy má i C++, z historických důvodů pro ně nezavádělo název ► Moderní jazyky kopírované datové typy většinou nepoužívají ● .NET je zavedl aby vyšel vstříc zvyklostem programátorů v C++ ● Důvod je podobný jako důvod zavedení primitivních typů v Javě Copyright © 2009, Rudolf Pecinovský ICZ 16 Dělení hodnotových objektů ► Proměnné ● Jejich hodnota se může v průběhu života změnit ● Nesmějí se používat v některých situacích ► Neměnné ● Hodnotu, která jim byla přiřazena „při narození“ zastupují až do své „smrti“ ● Chovají se obdobně jako hodnoty primitivních typů a také je s nimi možno obdobně zacházet ● Metody, které mají měnit hodnotu objektu, musejí vracet jiný objekt s touto změněnou hodnotou ► Všechny hodnotové typy bychom měli definovat jako neměnné ● Neplatí jen pro Javu, ale i pro jazyky, které umožňují pracovat přímo s objekty a ne jenom s odkazy (C++, C#) Copyright © 2009, Rudolf Pecinovský ICZ 17 Důsledky proměnnosti objektů ► Hodnota objektu se může změnit uprostřed výpočtu (jako kdyby se z pětek staly šestky) ► Se změnou hodnoty objektu se obecně mění i jeho hash-code => ► Uložíme-li objekt do kontejneru implementovaného pomocí hashové tabulky a po změně hodnoty jej tam už nenajdeme ► Používání proměnných hodnotových objektů výrazně snižuje robustnost a bezpečnost programů ► Proměnnost datových typů je nebezpečná zejména v programech, v nichž běží souběžně několik vláken ● Jazyky určené pro paralelní programování neměnné typy silně protěžují Copyright © 2009, Rudolf Pecinovský ICZ 18 Doporučené k použití ► Neměnné musí zůstávat jen atributy, jejichž hodnoty používají metody equals(Object) a hashCode() ► Atributy neovlivňující výsledky těchto metod se mohou měnit, protože na nich hodnota instance nezávisí Datové typy Objektové Hodnotové Proměnné Primitivní Odkazové Neměnné ► Příklad: Atributy počítající počet použití dané instance, počet volání některé metody atd. ► V hodnotových třídách by měly všechny „změnové metody“ vytvářet nové instance Copyright © 2009, Rudolf Pecinovský ICZ 19 Příklad: Zlomek public class Zlomek extends Number { private final int c; //čitatel private final int j; //jmenovatel; public Zlomek plus(Zlomek z) { //this. return new Zlomek( this.c*z.j + z.c*j, this.j*z.j ); } public Zlomek krát(Zlomek z) { return new Zlomek( c * z.c, } //... Copyright © 2009, Rudolf Pecinovský ICZ j * z.j ); 20 Vaše jistota na trhu IT Obsah Přepravka (Crate, Transport Object) 013 ► Vzor Přepravka využijeme při potřebě sloučení několika samostatných informací do jednoho objektu, prostřednictvím nějž je pak možno tyto informace jednoduše ukládat nebo přenášet mezi metodami. Motivace ► Některé vlastnosti sestávají z více jednoduchých hodnot ● Pozice je definována jednotlivými souřadnicemi ► Při nastavování takových hodnot se používá více parametrů ● Příklad: setPozice(int x, int y) ► Takovéto hodnoty se špatně zjišťují, protože Java neumožňuje vrácení několika hodnot současně ► Možnosti: ● Zjišťovat každou složku zvlášť ( getX() + getY()) ● Definuje se speciální třída, jejíž instance budou sloužit jako přepravky pro přenášení dané sady hodnot ► Druhou možnost doporučuje návrhový vzor Přepravka ► V anglické literatuře bývá označován nebo Transport Object ● Eckel jej v „Thinking in Patterns“ označuje jako Messenger Copyright © 2009, Rudolf Pecinovský ICZ 22 Vlastnosti přepravky ► Přepravka je objekt určený k uložení skupiny údajů „pod jednu střechu“ a jejich společnému transportu ► Přepravku řadíme mezi kontejnery = objekty určené k uložení jiných objektů ► Oproti standardům definuje své atributy jako veřejné, aby tak zjednodušila a zefektivnila přístup k jejich hodnotám ► Aby nebylo možno atributy nečekaně měnit, definuje je jako konstantní ► Velmi často pro ni definujeme pouze konstruktor s parametrem pro každý atribut, ale žádné metody ► V řadě případů však přepravky definují přístupové metody, ale i řadu dalších užitečných metod Copyright © 2009, Rudolf Pecinovský ICZ 23 Definice přepravky Pozice public class Pozice { public final int x; public final int y; public Pozice( int x, int y ) { this.x = x; this.y = y; } } Copyright © 2009, Rudolf Pecinovský ICZ 24 Použití přepravky Pozice public class Světlo { private static final Barva ZHASNUTÁ = Barva.ČERNÁ; private final Elipsa žárovka; private final Barva barva; // ... Konstruktory a dříve definované metody public Pozice getPozice() { return new Pozice( žárovka.getX(), žárovka.getY() ); } public void setPozice( int x, int y ) { žárovka.setPozice( x, y ); } public void setPozice( Pozice p) { žárovka.setPozice( p.x, p.y ); } } Copyright © 2009, Rudolf Pecinovský ICZ Převádí akci na svoji přetíženou verzi 25 Vaše jistota na trhu IT Obsah Služebník (Servant) 014 ► Návrhový vzor Služebník použijeme v situaci, kdy chceme skupině tříd nabídnout nějakou další funkčnost, aniž bychom zabudovávali reakci na příslušnou zprávu do každé z nich. ► Služebník je třída, jejíž instance (případně i ona sama) poskytují metody, které si vezmou potřebnou činnost (službu) na starost, přičemž objekty, s nimiž (nebo pro něž) danou činnost vykonávají, přebírají jako parametry. Motivace ► Několik tříd potřebuje definovat stejnou činnost a nechceme definovat na několika místech stejný kód ► Objekt má úkol, který naprogramovat buď neumíme, nebo bychom jej sice zvládli, ale víme, že je úloha již naprogramovaná jinde ► Řešení: Definujeme či získáme třídu, jejíž instance (služebníci) budou obsluhovat naše instance a řešit úkoly místo nich ● Řešení pak bude na jednom místě a bude se snáze spravovat ► Postup se hodí i když připravujeme řešení, které chceme definovat dostatečně obecné, aby je mohli používat všichni, kteří je budou v budoucnu potřebovat, a přitom nevíme, kdo budou ti potřební Copyright © 2009, Rudolf Pecinovský ICZ 27 Implementace ► Služebník nepracuje sám, ale komunikuje s obsluhovanými instancemi ► Aby mohl instance bezproblémově obsluhovat, klade na ně požadavky, co všechno musejí umět ► Služebník proto: ● Definuje rozhraní (interface), v němž deklaruje své požadavky ● Jeho obslužné metody budou jako své parametry akceptovat pouze instance deklarovaného rozhraní ► Instance, která chce být obsloužena: ● Musí být instancí třídy implementující dané rozhraní, aby se mohla vydávat za jeho instanci ● Implementací rozhraní deklaruje, že umí to, co od ní služebník k její plnohodnotné obsluze požaduje Copyright © 2009, Rudolf Pecinovský ICZ 28 Služebník – diagramy tříd Klient posílá zprávu obsluhované instanci a ta předá požadavek služebníku, který jej realizuje; klient nemusí o služebníku vědět Klient posílá zprávu přímo služebníku a v parametru mu současně předává instanci, kterou má obsloužit Copyright © 2009, Rudolf Pecinovský ICZ 29 Příklad 1: Plynule posuvné objekty ► Objekty projektu Tvary se umí přesouvat pouze skokem ► Pokud bychom chtěli, aby se přesouvaly plynule, museli bychom do každého z nich přidat příslušné metody, které by však byly u všech tříd téměř totožné ► Seženeme si služebníka – instanci třídy Přesouvač ► Tito služebníci vyžadují od obsluhovaných objektů implementaci rozhraní IPosuvný deklarujícího metody: ● Pozice getPozice(); ● void setPozice( Pozice ● void setPozice( int x, pozice ); int y ); ► Zato nabízí možnost volat metody: ● void ● void ● void přesunO ( IPosuvný ip, int dx, int dy ); přesunNa( IPosuvný ip, int x, int y ); přesunNa( IPosuvný ip, Pozice pozice ); Copyright © 2009, Rudolf Pecinovský ICZ 30 Příklad 2: Blikající světlo ► Chceme po instancích třídy Světlo, aby uměly blikat zadanou dobu nezávisle na jiné činnosti ► Cyklus je nepoužitelný, protože po dobu jeho provádění ostatní činnosti stojí ● Takto není možno naprogramovat ukazatel směru jedoucího auta ► Využijeme služeb instancí třídy Opakovač, jejichž metody umějí opakovat klíčové činnosti svých parametrů ► Opakovač od obsluhovaných vyžaduje, aby implementovali rozhraní IAkční s metodou akce(), kterou bude opakovač zadaný-počet-krát opakovat ► Demo: D03-2: Blikání světla Copyright © 2009, Rudolf Pecinovský ICZ 31 Vaše jistota na trhu IT Obsah Prázdný objekt (Null Object) ► Prázdný objekt je platný, formálně plnohodnotný objekt, který použijeme v situaci, kdy by nám použití klasického prázdného ukazatele null přinášelo nějaké problémy – např. by vyžadovalo neustále testování odkazuj na prázdnost nebo by vedlo k vyvolání výjimky NullPointerException. 015 Motivace ► Vrací-li metoda v nějaké situaci prázdný odkaz (null), musí volající metoda tuto možnost kontrolovat, než získaný odkaz použije ► Vrácením odkazu na plnohodnotný objekt umožníme zrušit tyto testy ► Příklady: ● Žádná barva ● Žádný směr ● Prázdný iterovatelný objekt – viz další stránka ● Třída java.util.Collections ● public static final <T> Set<T> emptySet() ● public static final <T> List<T> emptyList() ● public static final <T> Map<T> emptyMap() Copyright © 2009, Rudolf Pecinovský ICZ 33 Příklad: Směr8 public enum Směr8 { //== HODNOTY VÝČTOVÉHO TYPU ============ VÝCHOD ( 1, 0, "S" SEVEROVÝCHOD( 1, -1, "SV" SEVER ( 0, -1, "S" SEVEROZÁPAD ( -1, -1, "SZ" ZÁPAD ( -1, 0, "Z" JIHOZÁPAD ( -1, 1, "JZ" JIH ( 0, 1, "J" JIHOVÝCHOD ( 1, 1, "JV" ŽÁDNÝ ( 0, 0, "@" ; ), ), ), ), ), ), ), ), ), // ... zbytek definice Copyright © 2009, Rudolf Pecinovský ICZ 34 Příklad: PrázdnýIterátor public class PrázdnýIterátor<E> implements Iterator<E> { public boolean hasNext() { return false; } public <E> next() { throw new NoSuchElementException(); } } public void remove() { throw new UnsupportedOperationException(); } Copyright © 2009, Rudolf Pecinovský ICZ 35 Příklad: PrázdnýIterable public class PrázdnýIterable<E> implements Iterable<E> { private static final PrázdnýIterable jedináček = new PrázdnýIterable(); public static <T> PrázdnýIterable<T> getInstance() { return jedináček; } } public Iterator<E> iterator() { return PrázdnýIterátor.getInstance(); } Copyright © 2009, Rudolf Pecinovský ICZ 36 Vaše jistota na trhu IT 02 Obsah Vzory řešící počet instancí ► Společné vlastnosti ► Knihovní třída (Library class, Utility) ► Jedináček (Singleton) ► Výčtový typ (Enumeration) ► Fond (Pool) ► Originál (Original) ► Muší váha (Flyweight) 02 020 021 022 023 025 024 026 Vaše jistota na trhu IT Obsah Společné vlastnosti vzorů řešících problematiku počtu instancí 020 Motivace ► Zabezpečit dodržení zadaných pravidel pro vznik a případný zánik instancí ► Aby mohla třída za něco ručit, musí se o vše postarat sama a nenechat si do toho mluvit ► Třída poskytuje globální přístupové body k vytvořeným instancím – tovární metody ● Rozhodnou zda vytvořit instanci či vrátit některou starší ● Vyberou třídu, jejíž instance se vytvoří ● Mohou před vlastním vytvořením instance provést nějaké pomocné akce Copyright © 2009, Rudolf Pecinovský ICZ 39 Soukromý konstruktor ► Potřebují mít „v rukou“ vytváření instancí, takže nesmí nikomu umožnit, aby je vytvářel po svém ► Konstruktor vytvoří při každém zavolání novou instancí => je třeba jej znepřístupnit ► Definují konstruktor jako soukromý, a zveřejní místo něj tovární metodu ● Nikdo nemá šanci vytvořit instanci bez vědomí mateřské třídy ● Třída může rozhodovat, zda se vytvoří nová instance, nebo zda se použije existující ● Existence konstruktoru zamezí překladači vytvořit implicitní => je třeba jej vytvořit, i když žádný nechceme Copyright © 2009, Rudolf Pecinovský ICZ 40 Problémy se serializovatelností ► Načtení objektu z proudu obchází konstruktor ► Je třeba zabezpečit, aby i při načtení z proudu udržela třída kontrolu nad tvorbou svých instancí ► Třída musí rozhodnout, zda místo načteného objektu vrátí některý z existujících, anebo opravdu vytvoří nový ► Řešení závisí na realizaci ● Knihovny proudů ● Procesu serializace Copyright © 2009, Rudolf Pecinovský ICZ 41 Vaše jistota na trhu IT Obsah Knihovní třída (Library class, Utility) 021 ► Knihovní třída slouží jako obálka pro soubor statických metod. Protože k tomu nepotřebuje vytvářet instance, je vhodné jejich vytváření znemožnit. Charakteristika ► Slouží jako kontejner na metody, které nepotřebují svoji instanci ● Matematické funkce ● Pomocné funkce pro rodinu tříd ► Všechny metody jsou statické ► Nepotřebují instanci => neměly by ji ani umožnit vytvořit ● Definují soukromý konstruktor, aby místo něj nemohl překladač definovat vlastní verzi Copyright © 2009, Rudolf Pecinovský ICZ 43 Příklady ► java.lang.Math ► java.lang.Runtime ► java.lang.System ► java.util.Arrays ► java.util.Collections Copyright © 2009, Rudolf Pecinovský ICZ 44 Vaše jistota na trhu IT Obsah Jedináček (Singleton) 022 ► Jedináček specifikuje, jak vytvořit třídu, která bude mít nejvýše jednu instanci. Tato instance přitom nemusí být vlastní instancí dané třídy. Charakteristika ► Zabezpečuje vytvoření nejvýše jedné instance ● Tovární metoda vrací pokaždé odkaz na tutéž instanci jedináčka ► Většinou se vytváří instance vždy (tj. právě jedna), ale je-li její vytvoření drahé, lez použít odloženou inicializaci (je ale náročnější) ► Tovární metoda může vracet i instance potomků ● Je-li to účelné, může třída rozhodnout, zda nechá vytvořit instanci svoji či některého ze svých potomků ● Tovární metoda pak bude vracet odkaz na tu instanci, která byla při inicializaci vytvořena ► Příklady: Schránka (clipboard), Plátno Copyright © 2009, Rudolf Pecinovský ICZ 46 Realizace ► Odkaz na jedináčka je uložen ve statickém atributu ► Není-li požadována odložená inicializace, lze atribut inicializovat již v deklaraci (nejlepší řešení) ► Je třeba zabezpečit, aby všechny objekty, které tato inicializace používá, byly již validní ► Odložená inicializace přináší problémy v programech, které používají vlákna či podprocesy ► Je třeba ošetřit neklonovatelnost a případnou serializaci Copyright © 2009, Rudolf Pecinovský ICZ 47 Ukázka public class Jedináček { private static final Jedináček JEDINÁČEK = new Jedináček(); public static Jedináček getInstance() { return JEDINÁČEK; } } Copyright © 2009, Rudolf Pecinovský ICZ 48 Výhody oproti statickému atributu ► Jedináček: jediná instance třída je získávána voláním veřejné tovární metody ► Atribut: Odkaz na jedinou instanci je uložen ve veřejném statickém atributu ► Výhody atributu ● Trochu méně psaní ● Většinou nepatrně větší rychlost ► Výhody jedináčka ● Snazší dynamická kontrola nad poskytováním přístupu ● Otevřenější vůči případným budoucím úpravám (např. náhrada jedináčka fondem) Copyright © 2009, Rudolf Pecinovský ICZ 49 Jednovláknová odložená inicializace public static Jedináček getInstance() { if (JEDINÁČEK == null) { JEDINÁČEK = new Jedináček(); } return JEDINÁČEK; } Copyright © 2009, Rudolf Pecinovský ICZ 50 Problémy při více vláknech První vlákno Druhé vlákno Jedináček.getInstance() { if( JEDINÁČEK == null ) { Jedináček.getInstance() { if( JEDINÁČEK == null ) { JEDINÁČEK = new Jedináček(); } return JEDINÁČEK; } JEDINÁČEK = new Jedináček(); } return JEDINÁČEK; } Copyright © 2009, Rudolf Pecinovský ICZ 51 Problémy při více vláknech První vlákno Druhé vlákno Jedináček.getInstance() { if( JEDINÁČEK == null ) { Jedináček.getInstance() { if( JEDINÁČEK == null ) { JEDINÁČEK = new Jedináček(); } return JEDINÁČEK; } JEDINÁČEK = new Jedináček(); } return JEDINÁČEK; } Copyright © 2009, Rudolf Pecinovský ICZ 52 Možné řešení private static volatile Jedináček JEDINÁČEK = null; public static Jedináček getJedináček() { if (JEDINÁČEK == null ) { synchronized( Jedináček.class ) { if (JEDINÁČEK == null ) { JEDINÁČEK = new Jedináček(); } } } return JEDINÁČEK; } Copyright © 2009, Rudolf Pecinovský ICZ 53 Současné doporučované řešení public class Jedináček { private Jedináček() { } public static Jedináček getInstance() { return Schránka.INSTANCE; } private static class Schránka { private static final Jedináček INSTANCE = new Jedináček(); } } Copyright © 2009, Rudolf Pecinovský ICZ 54 Vaše jistota na trhu IT Obsah Výčtový typ (Enumeration) 023 ► Výčtové typy slouží k definici skupin předem známých hodnot a k umožnění následné typové kontroly. Vedle klasických hodnotových výčtových typů umožňuje Java definovat i Funkční výčtové typy, u nichž se jednotlivé hodnoty liší reakcemi na zasílané zprávy. Charakteristika ► Předem pevně daný počet instancí s předem pevně danými hodnotami ► Ekvivalent enum v C++, ale s OO nadstavbou a důslednou typovou kontrolou ► Výhody ● Důsledná typová kontrola ● V kontejnerech instancí lze využít známý maximální počet ► Problémy ● Je třeba ošetřit serializovatelnost a klonovatelnost ● Standardní knihovna Javy ji řeší Copyright © 2009, Rudolf Pecinovský ICZ 56 Implementace ► Sada statických atributů odkazujících na instance své třídy ► Atributy jsou inicializovány v deklaraci ► Jazyk často nabízí prostředky na efektivní použití v přepínačích a cyklech s parametrem ● Umí vrátit vektor seřazených hodnot ● Umí vrátit instanci se zadaným pořadím ● Umí vrátit pořadí zadané instance ve výčtu Copyright © 2009, Rudolf Pecinovský ICZ 57 Syntaxe výčtových typů ► V definicích výčtových typů je klíčové slovo class nahrazeno slovem enum ► Za hlavičkou třídy následuje seznam hodnot daného typu ► Za čistým výčtem (jenom výčet bez metod) nemusí následovat středník; jakmile však výčet není osamocen, středník je nutný (raději psát vždy) ► Výčtový typ překladač přeloží stejně jako kdyby byl definován jako potomek třídy java.lang.Enum public enum Období { JARO, LÉTO, PODZIM, ZIMA; } Copyright © 2009, Rudolf Pecinovský ICZ 58 Přeložený tvar čistého výčtu package výčty; public final class Období extends Enum { public static final Období JARO; public static final Období LÉTO; public static final Období PODZIM; public static final Období ZIMA; private static final Období $VALUES[]; static { JARO = new Období("JARO", 0); LÉTO = new Období("LÉTO", 1); PODZIM = new Období("PODZIM", 2); ZIMA = new Období("ZIMA", 3); Deklarace hodnot (veřejných konstant) daného typu Pole hodnot daného typu Statický inicializační blok inicializující konstanty aMetoda pole s odkazy ně vracejícína pole hodnot daného typu Metoda vracející hodnotu po zadání jejího názvu $VALUES = new Období[] { JARO, LÉTO, PODZIM, ZIMA }; } public static final Období[] values() { return (Období[])((Období []) ($VALUES)).clone(); } public static Období valueOf(String name) { return (Období)Enum.valueOf(výčty.Období, name); } private Období(String s, int i) { Soukromý konstruktor super(s, i); zabraňující vytváření } } dalších instancí ICZ Copyright © 2009, Rudolf Pecinovský 59 Metody zděděné od třídy Enum ► String name() Vrátí název dané instance ► int ordinal() Vrátí pořadí deklarace dané instance (začíná se nulou) ► boolean equals(Object other) Vrací true je-li instance totožná s parametrem ► int compareTo(E o) Vrátí -1, 0 či +1 podle toho, je-li deklarace daná instance menší, rovna či větší než parametr (porovnává se ordinal) ► E[] values() ► E valueOf(String name) Jsou zde uvedeny pro úplnost, nedědí se, dodá je překladač (viz předchozí snímek) Překrytá verze rodičovské metody (volá rodiče a přetypuje výsledek) Copyright © 2009, Rudolf Pecinovský ICZ 60 „Chytřejší“ výčtové typy ► Často je výhodné, aby hodnoty výčtového typu vykazovaly jistou „dodatečnou inteligenci“ ► Výčtové typy nemusí být pouze čistým výčtem hodnot, jsou to standardní typy ● Mohou mít vlastní atributy a vlastnosti ● Mohou mít vlastní metody ● Mohou využívat konstruktoru s parametry ► Příklad: Směr8 Copyright © 2009, Rudolf Pecinovský ICZ 61 Vytváření instancí třídy Směr8 public enum Směr8 { Deklarace hodnot //== HODNOTY VÝČTOVÉHO TYPU ================================================== VÝCHOD ( 1, 0, "V", "VYCHOD" ), (veřejných konstant) SEVEROVÝCHOD( 1, -1, "SV", "SEVEROVYCHOD" ), daného typu SEVER ( 0, -1, "S", "SEVER" ), SEVEROZÁPAD ( -1, -1, "SZ", "SEVEROZAPAD" ), ZÁPAD ( -1, 0, "Z", "ZAPAD" ), JIHOZÁPAD ( -1, 1, "JZ", "JIHOZAPAD" ), JIH ( 0, 1, "J", "JIH" ), JIHOVÝCHOD ( 1, 1, "JV", "JIHOVYCHOD" ), ; //== KONSTANTNÍ ATRIBUTY TŘÍDY =============================================== PřepravkaMapa pro dočasné pro získání instance public static final int SMĚRŮ = 8; uchování hodnot zadaných zadaného názvu či zkratky private static final int MASKA = 7; Konstruktor ukládá private static final Map<String,Směr8> názvy =konstruktoru, všechny parametry než je budeSMĚRŮ*3 možno uložit new HashMap<String,Směr8>( ); do přepravky, private static final int[][] posun = new do int[SMĚRŮ][2]; aby je bylo možno mapy a příslušných polívyzvednout private static final Směr8[] SMĚRY = values(); ve statickém inicializačním bloku Instance si svá data pamatují static { for( Směr8 s : SMĚRY ) { v polích, která jsou atributy třídy posun[s.ordinal()][0] = s.přepravka.dx; posun[s.ordinal()][1] = s.přepravka.dy; Statický inicializační blok názvy.put( s.přepravka.zkratka, s ); inicializující pole a mapu názvy.put( s.přepravka.název, s ); názvy.put( s.přepravka.názevBHC,s ); hodnotami z přepravky s.přepravka = null; } } Copyright © 2009, Rudolf Pecinovský ICZ 62 Výběr rozšiřujících metod třídy Směr8 public Směr8 čelemVzad() { return SMĚRY[MASKA & (4+ordinal())]; } public int dx() { return posun[ordinal()][0]; } public int dalšíX( int x, int vzdálenost ) { return x + dx()*vzdálenost; } public Pozice dalšíPozice( Pozice pozice, int vzdálenost ) { return new Pozice( dalšíX( pozice.x, vzdálenost ), dalšíY( pozice.y, vzdálenost ) ); } Copyright © 2009, Rudolf Pecinovský ICZ 63 Funkční výčtový typ ► Jednotlivé hodnoty výčtového typu mohou být instancemi jeho podtříd ► Každá hodnota pak bude na danou zprávu (volání metody) reagovat jinak ► Oblíbené použití: ● Každá instance představuje operaci, takže se v závislosti na výběru instance provede příslušná operace ► Příklady ● Výčtový typ operací ( např. PLUS, MINUS, KRAT, DELENO) ● Výčtový typ zdrojů (např. KLÁVESNICE, SOUBOR, SÍŤ) ● Třída Člověk v doprovodných programech ● Diamanty (obrázek) Copyright © 2009, Rudolf Pecinovský ICZ 64 Příklad funkčního výčtového typu public enum Operace { ► Metody instancí PLUS { musí překrývat public int proveď( int a, int b) { metodu předka, return a + b; protože jinak by } nebyly zvenku vidět }, ► Metody mohou definovat i „funkční vlastnosti“ instancí – např. jak se daná instance-tvar nakreslí – viz Diamanty Copyright © 2009, Rudolf Pecinovský MINUS { public int proveď( int a, int b) { return a - b; } }; public abstract proveď( int a, int b ); } //... Operace op = PLUS; int výsledek = op.proveď( x, y ); //... ICZ 65 Příklad 2: Člověk 1/2 public enum ČlověkE50 { LENOCH { public void budíček() { System.out.println("Pomalu vstávám" ); } public void práce() { System.out.println("Líně pracuji" ); } public void volno() { System.out.println("Odcházím spát" ); } public void spánek() { System.out.println("Stále spím" ); } }, ČILOUŠ { public void budíček() { System.out.println("Rychle vstávám" ); } public void práce() { System.out.println("Čile pracuji" ); } public void volno() { System.out.println("Aktivně odpočívám" ); } public void spánek() { System.out.println("Omdlím a spím" ); } }, PRACANT { public void budíček() { System.out.println("Brzy vstávám" ); } public void práce() { System.out.println("Zaníceně pracuji" ); } public void volno() { System.out.println("Stále pracuji" ); } public void spánek() { System.out.println("Usínám nad prací" ); } }; abstract public void budíček(); abstract public void práce(); abstract public void volno(); abstract public void spánek(); Copyright © 2009, Rudolf Pecinovský ICZ 66 Příklad 2: Člověk 2/2 /********************************************************** * Definice pracovního dne člověka. */ private static void den( ČlověkE50 č ) { System.out.println("Pracovní den instance " + č); č.budíček(); č.práce(); č.volno(); č.spánek(); } public den( den( den( } static void test() { LENOCH ); ČILOUŠ ); PRACANT ); public static void main( String[] args ) test(); } { } Copyright © 2009, Rudolf Pecinovský ICZ 67 Vrstvený výčtový typ ► Někdy potřebujeme, aby sada dostupných instancí závisela na jistých okolnostech ● Příklad: Směr360 – Směr8 – Směr4 ► Standardní výčtový typ v Javě nepodporuje dědičnost (je třeba se vrátit ke „klasice“) ● Nelze vytvořit explicitního potomka třídy Enum ● Není možné vytvořit potomka výčtového typu ► Potomek musí být speciálním případem předka ► Pozor na problémy s typy návratových hodnot Copyright © 2009, Rudolf Pecinovský ICZ 68 Vaše jistota na trhu IT Obsah Fond (Pool) 024 ► Fond využijeme ve chvíli, kdy potřebujeme z nějakého důvodu omezit počet vytvořených instancí a místo vytváření instancí nových dáme přednost „reinkarnaci“ (znovuoživení) instancí dříve použitých a v danou chvíli již nepoužívaných. Charakteristika ► Používá se v situacích, kdy potřebujeme omezit počet existujících instancí nějaké třídy … ● Připojení k databázi ● Objekty náročné na různé zdroje ► … případně počet instancí vytvářených ● Objekty, jejichž vytvoření je drahé a důvodně předpokládáme, že je brzy bude chtít někdo využít ● Vlákna, animované objekty, … ► Vždy, když objekt potřebujeme, požádáme o něj fond, a po použití objekt zase do fondu vrátíme ► Umí-li si objekt sám zjistit, že jeho práce skončila, můžeme explicitní vracení objektu fondu vypustit ● Vlákna Copyright © 2009, Rudolf Pecinovský ICZ 70 Možné přístupy ► Pevný počet instancí s frontou požadavků ● Je předem dán maximální počet instancí ● Objeví-li se víc požadavků, než je povolený počet instancí, nepokryté požadavky se řadí do fronty, kde čekají, až se některá z instancí uvolní ● Typické použití: databázová připojení ► Dynamické zvyšování počtu instancí ● Počet instancí se dynamicky zvyšuje podle potřeby ● Když přijde požadavek a všechny instance jsou „rozebrané“, vytvoří se nová ● Typické použití: instance, jejichž vytvoření je „drahé“ a vyplatí se je proto nerušit, ale místo toho znovu použít ● Příklad: vlákna, animované objekty Copyright © 2009, Rudolf Pecinovský ICZ 71 Implementace – řešené problémy ► Vyhledávání volných instancí ● Je-li instancí ve fondu málo (typicky do 5), lze vyhledat prázdnou instancí projitím pole instancí ● Je-li jich víc, dvě možnosti: ● Ukládat přidělené do mapy, kde ji bude možno po uvolnění rychle vyhledat a přeřadit mezi volné ● Ukládat instance do zřetězeného seznamu (přidělená se přesune z počátku na konec, uvolněná se přesune naopak na počátek) ► Přidělování a vracení jednotlivých instancí ● Jak zabezpečit, aby objekt, který instanci vrátil, už nemohl danou instanci používat ● Např. když si instanci půjčenou z fondu předává několik metod a jedna metoda instanci vrátí dřív, než ji jiná přestane používat ● Jak zjistit, kdo instanci zapomněl vrátit Copyright © 2009, Rudolf Pecinovský ICZ 72 Přidělování a vracení instancí ► Použití vrácené či cizí instance ● Instance dostane po přidělení jednoznačné ID, které bude parametrem všech jejích metod a jehož prostřednictvím se bude oprávněný „vlastník“ instance vždy identifikovat ● Po vrácení instance bude ID odebráno a nový vlastník dostane přidělen nové ID ► Zapomenuté uvolnění ● Při přidělení instance se může vytvořit a zapamatovat výjimka, která jednoznačně identifikuje místo přidělení i žadatele – tu si bude instance pamatovat a na požádání uložené informace prozradí ► Bezpečné řešení zdržuje, takže je v časově kritických případech lze modifikovat tak, že se bude kompletně spouštět pouze v režimu ladění Copyright © 2009, Rudolf Pecinovský ICZ 73 Příklad ► IFond ► Fond ► Brownův pohyb molekul Copyright © 2009, Rudolf Pecinovský ICZ 74 Příklad: Rozhraní IFond package rup.česky.vzory._12_fond; public interface IFond<T> { /********************************************************** * Vrátí volnou instanci; není-li v rezervoáru žádná * volná instance, vytvoří novou. */ public T dejIhned(); /********************************************************** * Vrátí volnou instanci; není-li v rezervoáru žádná * volná instance, zařadí žadatele do fronty čekajících. */ public T dejPočkám(); /********************************************************** * Vrátí volnou instanci; není-li v rezervoáru žádná * volná instance, vrátí null; */ public T dejVolnou(); /********************************************************** * Předá do rezervoáru dále nepotřebnou instanci. */ public void vracímInstanci( T instance ); } Copyright © 2009, Rudolf Pecinovský ICZ 75 Vaše jistota na trhu IT Obsah Originál (Original) 025 ► Návrhový vzor Originál použijeme v situaci, kdy budeme dopředu vědět, že se v aplikaci bude používat pouze malý počet různých instancí, avšak tyto instance budou požadovány na řadě míst kódu. Charakteristika ► Hodnotový neměnný typ, který zabezpečuje, že od každé hodnoty bude existovat jen jedna instance ► Počet hodnot není předem omezen, vychází však často ze společné výchozí sady ► Nevýhody ● Při žádosti o instanci je třeba zjistit, zda již neexistuje ► Výhody ● Zbytečně se nám nekumulují duplicitní instance ● Není třeba používat equals ► Příklad: Barva v knihovně Tvary Copyright © 2009, Rudolf Pecinovský ICZ 77 Implementace ► Tovární metody specifikují parametry potřebné k určení požadované hodnoty ► Alternativně je možno přiřadit instanci název a instance pak vybírat z mapy jako u výčtových typů ► Pro zrychlení vyhledávání se přehled o existujících instancích se udržuje ve formě mapy <Hodnota,Instance> příp. <Název,Instance> ► Má smysl, pokud instance málo vytváříme, ale často používáme – šetříme tak čas správce paměti ► Příklad: rup.česky.tvary.Barva Copyright © 2009, Rudolf Pecinovský ICZ 78 Vaše jistota na trhu IT Obsah Muší váha (Flyweight) 026 ► Vzor označovaný jako Muší váha používáme ve chvíli, kdy řešení problému vyžaduje vytvoření značného množství objektů. Ukazuje, jak je možné toto množství snížit tím, že místo skupiny objektů s podobnými charakteristikami použijeme jeden sdílený objekt tak, že z něj vyjmeme část jeho stavu, která odlišuje jednotlivé zastupované objekty, a volané metody se tyto informace v případě potřeby dozvědí z vnějšího zdroje prostřednictvím svých parametrů. Charakteristika ► Řeší situace, které při standardním přístupu vyžadují vytvoření příliš velkého množství objektů ► Vzor řeší problém tak, že jeden objekt slouží jako zástupce několika „virtuálních“ objektů ► Příklady ● V textovém editoru není představován každý znak v dokumentu samostatným objektem, ale všechny stejné znaky zastupuje představitel daného znaku ● V situacích, kde vystupuje malá množina různých objektů v řadě různých navzájem si velmi podobných mutacích (viz hra rup.česky.vzory._13_muší_váha.diamanty.Diamanty) Copyright © 2009, Rudolf Pecinovský ICZ 80 Implementace ► Stav objektu je rozdělen do dvou částí ● Vnitřní stav, který nese klíčové informace o objektu a který je společný všem „virtuálním“ objektům zastupovaným daným objektem ● Vnější stav, v němž se jednotlivé zastupované objekty liší, a který je proto předáván metodám v parametrech ► Příklady ● Textový editor: ● Vnitřním stavem objektu je zastupovaný znak a konkrétní formát, ● Vnějším stavem je pozice znaku v dokumentu ● Diamanty (pgm) ● Vnitřním stavem je vzhled obrazce, ● Vnějším stavem je jeho umístění na hrací desce Copyright © 2009, Rudolf Pecinovský ICZ 81 Příklad: Hra Diamanty ► rup.česky.vzory._13_muší_váha.diamanty.Diamanty ► Všechna místa na hrací desce, na nichž se nachází diamant daného tvaru, se odkazují na stejný objekt ► Diamant zná svůj tvar, ale od programu se dozví, na kterém místě se právě nachází Copyright © 2009, Rudolf Pecinovský ICZ 82 Vaše jistota na trhu IT 03 Obsah Skrývání implementace 03 ► Zástupce (Proxy) 031 ► Příkaz (Command) 032 ► Iterátor (Iterator) 033 ► Stav (State) 034 ► Šablonová metoda (Template Method) 035 Vaše jistota na trhu IT Obsah Zástupce (Proxy) ► Zavádí zástupný objekt, který odstiňuje zastupovaný objekt od jeho uživatelů a sám řídí přístup uživatelů k zastupovanému objektu. 031 Druhy zástupců 1/2 ► Vzdálený zástupce (remote proxy) ● Lokální zástupce vzdáleného objektu (zastupuje objekt v jiném adresovém prostoru, na jiném VM, na jiném počítači, …) ● Java RMI, CORBA, XML/SOAP, … ► Virtuální zástupce (virtual proxy) ● Potřebujeme-li odložit okamžik skutečného vytvoření objektu (vytvoření objektu je drahé – load on demand) ● Rozlišuje mezi vyžádáním objektu a jeho skutečným použitím ● Transparentní optimalizace (např. cache) ► Chytrý odkaz (smart reference) ● Doplňuje komunikaci s objektem o doprovodné operace (počítání odkazů, správa paměti, logování, …) ● Příklad: debugger mající za úkol zastavit při n-tém přístupu k objektu Copyright © 2009, Rudolf Pecinovský ICZ 85 Druhy zástupců 2/2 ► Ochranný zástupce (protection proxy) ● Potřebujeme-li skrýt pravou identitu objektu ● Je-li třeba ověřovat přístupová práva volajících objektů ● Je-li třeba rozšířeně validovat parametry ► Modifikační zástupce (Copy-on-write proxy) ● Opožděné kopírování objektů až při jejich modifikaci ► Synchronizační zástupce (Synchronization proxy) ● Transparentní synchronizace vláken při přístupu k objektu Copyright © 2009, Rudolf Pecinovský ICZ 86 Implementace ► Zástupce obaluje zastupovaný objekt a zprostředkovává komunikaci okolního programu se zastupovaným objektem ► V převážné většině případů je odkaz na zastupovaný objekt atributem zástupce, kterému jsou po případné kontrole předány požadavky a naopak obdržené výsledky jsou předány žadateli ► V případě ochranného zástupce lze při mírnějších požadavcích na ochranu (nehrozí-li záměrný útok) „skrýt“ zastupovaný objekt za pouhé rozhraní Copyright © 2009, Rudolf Pecinovský ICZ 87 Diagram tříd zástupců Copyright © 2009, Rudolf Pecinovský ICZ 88 Vzdálený zástupce (Remote proxy) ► Zastupuje objekt umístěný jinde (často na jiném VM) ► Zprostředkovává komunikaci mezi zastupovaným vzdáleným objektem a objekty z okolí zástupce ► Úkol: zapouzdřit a skrýt detaily komunikace ● Klienti komunikují se zastupovaným objektem jako by byl místní ● Klient nemusí vůbec poznat, v kterém okamžiku je zastupován místní objekt a ve které je zastupován skutečně vzdálený objekt ► Musí být stále připraven na možnost selhání komunikace a být schopen vyhodit výjimku Copyright © 2009, Rudolf Pecinovský ICZ 89 Vzdálený zástupce – schéma Klient KProxy SProxy Server Zpráva objektu Přenos požadavku Předání požadavku Vrácení výsledku Přenos výsledku Vrácení výsledku Copyright © 2009, Rudolf Pecinovský ICZ 90 Ochranný zástupce (protection proxy) ► Umožňuje zatajit skutečný typ zastupovaného objektu ► Definuje pouze ty metody, které má objekt umět ► Může doplnit kontrolu přístupových práv ► Možné implementace ● Skrýt skutečnou třídu za rozhraní – použitelné, pokud chceme pouze zjednodušit (tj. zpřehlednit) rozhraní ● Vytvořit skutečného zástupce, tj. objekt, který obsahuje odkaz na zastupovaný objekt, jemuž předává zprávy a od nějž přebírá odpovědi – potřebujeme-li ochránit před možným útokem Copyright © 2009, Rudolf Pecinovský ICZ 91 Ukázka kódu ochranného zástupce public class Zástupce { private Zastupovaný z; Zástupce( Zastupovaný zz ) { z = zz } Zástupce( Object... parametry ) { z = new Zastupovaný( parametry ); } public int metoda( Object... parametry ) { return z.metoda( parametry ); } } Copyright © 2009, Rudolf Pecinovský ICZ 92 Příklad: Rozhraní IZastávka package rup.česky.vzory._14_zástupce.doprava; public interface IZastávka extends IKreslený { /************************************************* * Vrátí linku, na které zastávka leží. */ public Linka getLinka(); /************************************************* * Vrátí pozici zastávky, tj. jejího středu. */ public Pozice getPozice(); /************************************************* * Vrátí odkaz na předchozí zastávku na trati. */ public IZastávka getPředchozí(); /************************************************* * Vrátí odkaz na následující zastávku na trati.*/ public IZastávka getNásledující(); } Copyright © 2009, Rudolf Pecinovský ICZ 93 Příklad: Třída Zastávka private class Zastávka implements IZastávka { //== KONSTRUKTORY A TOVÁRNÍ METODY ============================ public Zastávka( Linka linka, int x, int y ); public Zastávka( Zastávka předchozí, int x, int y ); private Zastávka(Zastávka předchozí,Linka linka,int x,int y); //== VEŘEJNÉ METODY INSTANCÍ ================================== public Linka getLinka(); public Pozice getPozice(); public IZastávka getPředchozí(); public IZastávka getNásledující(); public public public public public void nakresli(Kreslítko g); String toString(); IZastávka napoj( int x, int y ); void napojNa( Zastávka předchozí ); Zastávka zruš(); } Copyright © 2009, Rudolf Pecinovský ICZ 94 Příklad: java.util.Collections ► public static <E> Xyz<E> checkedXyz(Xyz<E> c, Class<E> type) ► public static <T> Xyz<T> unmodifiableXyz(Xyz<? extends T> c) ► Za Xyz lze dosadit: ● Collection ● Set ● List ● Map ● SortedSet ● SortedMap Copyright © 2009, Rudolf Pecinovský ICZ 95 Virtuální zástupce (virtual proxy) ► Použijeme jej, když je vytváření objektu drahé a objekt ve skutečnosti není potřeba od začátku celý ► Zástupe vytvoří zastupovaný objekt (např. načte obrázek), až když je doopravdy potřeba ► Do okamžiku skutečného vytvoření může ● Nahrazovat volání metod zastupovaného objektu voláním vlastních náhradních metod (např. dotaz na požadovaný paměťový prostor pro budoucí obrázek) ● Připravit a uchovávat seznam konfiguračních parametrů, které se použijí v okamžiku skutečného vytvoření objektu Copyright © 2009, Rudolf Pecinovský ICZ 96 Chytrý odkaz (smart reference) ► Umožňuje doplnit komunikaci s objektem o další akce ● Kontrola přístupových práv k objektu ● Evidence požadavků na služby objektu ● Zamčení objektu při zápisu ► Umožňuje zefektivnit práci s objektem ● Při první žádosti o objekt se objekt nevytváří, ale zavádí se do paměti dříve vytvořený objekt uložený v nějaké vnější paměti ● Udržuje spojení s databází ještě chvíli po poslední žádosti ► Virtuální zástupce je druh chytrého odkazu ● Musí se umět rozhodnout, na co stačí sám, kdy už je třeba zastupovaný objekt skutečně vytvořit Copyright © 2009, Rudolf Pecinovský ICZ 97 Synchronizační zástupce ► Příklad: synchronizované kolekce ve třídě java.util.Collections získané voláním metod public static <T> Xyz<T> synchronizedXyz(Xyz<T> c) ► Za Xyz lze dosadit: ● Collection ● Set ● List ● Map ● SortedSet ● SortedMap Copyright © 2009, Rudolf Pecinovský ICZ 98 Vaše jistota na trhu IT Obsah Příkaz (Command) 032 ► Zabalí metodu do objektu, takže s ní pak lze pracovat jako s běžným objektem. To umožňuje dynamickou výměnu používaných metod za běhu programu a optimalizaci přizpůsobení programu požadavkům uživatele. Motivace ► Občas víme, že se v nějaké situaci má něco provést, ale při psaní programu nemáme ani vzdálenou představu o tom, co to bude ● Víme, že při stisku tlačítka má program nějak zareagovat, ale až při vložení konkrétního tlačítka do konkrétního GUI budeme tušit, jaká je ta správná reakce. Při definici objektu Tlačítko o tom však nemáme ani vzdálené tušení ► Chceme oddělit objekt, který spouští nějakou akci, od objektu, který ví, jak ji vykonat ► Potřebovali bychom, aby se s akcemi mohlo pracovat stejně jako s daty 1. Připravíme „proměnnou“ 2. Až budeme vědě, co se má dělat, vložíme do proměnné kód 3. Až budeme potřebovat kód provést, vytáhneme jej z proměnné a provedeme Copyright © 2009, Rudolf Pecinovský ICZ 100 Implementace ► Vzor doporučuje zabalit akci (skupinu akcí) do objektu a tím převést akci na data ► Definujeme rozhraní specifikující charakteristiku (signaturu) požadovaných metod ► Vytvoříme objekty implementující toto rozhraní a realizující jednotlivé typy akcí ● Příslušné třídy mívají jedinou instanci, i když často nebývají definovány jako jedináčci ► Podle toho, jakou akci je třeba realizovat, naplní se vyhrazená proměnná odkazem na příslušný akční objekt Copyright © 2009, Rudolf Pecinovský ICZ 101 Příkaz × Služebník ► Služebník: Mám obsluhované objekty a hledám někoho, kdo by je „obsloužil“, tj. kdo by s nimi (nad nimi) provedl požadovanou operaci ► Příkaz: Mám připravený, resp. připravuji příkaz (akci, obsluhu) který vyžaduje provedení nějaké akce, resp. nějakých akcí. Definuji rozhraní (interface) s požadavky na objekt, který by vykonání požadované akce zabezpečil Copyright © 2009, Rudolf Pecinovský ICZ 102 Vyhledání položky 1/2 ► K vyhledání prvku v setříděném polí slouží metody Arrays.binarySearch(T[] ta, T klíč) Arrays.binarySearch(T[] ta, T klíč, Comparator<? super T> c) kde typ T může být libovolný primitivní či objektový typ ► K vyhledání prvku v setříděném seznamu slouží metody Collections.binarySearch(List<T> lt, T klíč) Collections.binarySearch(List<T> ta, T klíč, Comparator<? super T> c) ► K získání komparátoru, který třídí v obráceném pořadí, slouží Collections.reverseOrder() Collections.reverseOrder(Comparator<T> cmp) Copyright © 2009, Rudolf Pecinovský ICZ 103 Třídění a vyhledání maxima/minima ► K setřídění kolekce slouží metody List.sort(List<T> coll) List.sort(List<T> coll, Comparator<? super T> c) ► Obdobně je možné třídit pole voláním metod z třídy java.util.Arrays ► K vyhledání největšího prvku v kolekci slouží metody Collections.max(Collection<T> coll) Collections.max(Collection<T> coll, Comparator<? super T> c) ► K vyhledání nejmenšího prvku v kolekci slouží metody Collections.min(Collection<T> coll) Collections.min(Collection<T> coll, Comparator<? super T> c) Copyright © 2009, Rudolf Pecinovský ICZ 104 Zdrojový kód metody max(Collection<T>) //Deklarace typových parametrů jsou zjednodušené public static <T extends Comparable<T>> Kolekcí budeme procházet T max(Collection<T> coll) pomocí iterátoru { Iterator<T> i = coll.iterator(); T candidate = i.next(); while(i.hasNext()) { T next = i.next(); if (next.compareTo(candidate) > 0) candidate = next; } return candidate; } Připravíme si kandidáta na maximum Procházíme zbytkem kolekce, a je-li někdo větší než kandidát, prohlásíme jej za lepšího kandidáta ► Příklad převzat z knihovní třídy java.util.Collections Copyright © 2009, Rudolf Pecinovský ICZ 105 Zdrojový kód verze s komparátorem //Deklarace typových parametrů jsou zjednodušené public static <T> T max(Collection<T> coll, Comparator<T> comp) { if (comp==null) return (T)max((Collection<SelfComparable>) (Collection) coll); Iterator<T> i = coll.iterator(); T candidate = i.next(); Je-li dodán komparátor, připraví si pokračuje iterátor Pak Je-li zadán prázdný a kandidáta najako maximum minule, odkazstejně na komparátor, s jinou metodou zkusímejenom verzi bez komparátoru porovnání hodnot while(i.hasNext()) { T next = i.next(); if (comp.compare(next, candidate) > 0) Verze bez komparátoru vyžaduje candidate = next; nativně porovnatelné objekty – } definuje si soukromé vnořené rozhraní, return candidate; na něž se pokusí instance přetypovat } private interface SelfComparable extends Comparable<SelfComparable> {} Copyright © 2009, Rudolf Pecinovský ICZ 106 Příklad: Třídění nativní a modulo public class Modulo2 implements Comparable<Modulo2> { Objekt má dva atributy: private final int hodnota; private final int modul; hodnotu a modul public Modulo2( int h ) { hodnota = h; modul = h % 10; }//============================================================= public String toString() { return String.format( "%2s", hodnota); }//============================================================= private static void tiskni( String s, Modulo2[] mm) { System.out.println( s + Arrays.asList( mm ) ); }//============================================================= public static final void test() { Random rnd = new Random(); Vyrobím pole objektů Modulo2[] mm = new Modulo2[20]; for( int i=0; i < mm.length; mm[i++] = new Modulo2(rnd.nextInt(100)) ); tiskni( "Výchozí: ", mm ); Arrays.sort( mm ); tiskni( "Setříděné: ", mm ); Arrays.sort( mm, new Comp() ); tiskni( "Comparator:", mm ); }//============================================================= public int compareTo(Modulo2 m) { Setřídím je dvěma způsoby return (hodnota m.hodnota); ICZ 107 Copyright © 2009, Rudolf Pecinovský Příklad: Třídění nativní a modulo Výchozí: [99, 79, 18, 81, 47, 88, 88, 42, 56, 85, 61, 23, 41, 44, 3, 46, 74, 52, 17, 23] Setříděné: [ 3, 17, 18, 23, 23, 41, 42, 44, 46, 47, 52, 56, 61, 74, 79, 81, 85, 88, 88, 99] public class Modulo2 implements Comparable<Modulo2> Comparator:[41, 61, 81, 42, 52, 3, 23, 23, 44, 74, { private final int hodnota; 85, 46, 56, 17, 47, 18, 88, 88, 79, 99] private final int modul; public Modulo2( int h ) { hodnota = h; modul = h % 10; }//============================================================= public String toString() { return String.format( "%2s", hodnota); }//============================================================= private static void tiskni( String s, Modulo2[] mm) { System.out.println( s + Arrays.asList( mm ) ); }//============================================================= public static final void test() { Random rnd = new Random(); Modulo2[] mm = new Modulo2[20]; for( int i=0; i < mm.length; mm[i++] = new Modulo2(rnd.nextInt(100)) ); tiskni( "Výchozí: ", mm ); Arrays.sort( mm ); tiskni( "Setříděné: ", mm ); Arrays.sort( mm, new Comp() ); tiskni( "Comparator:", mm ); }//============================================================= public int compareTo(Modulo2 m) { return (hodnota - m.hodnota); ICZ Copyright © 2009, Rudolf Pecinovský 108 Příklad na vektor příkazů: Třída Kostka /** Generátor vzhledu vrchni strany kostky * při hození jednotlivých čísel. */ private static final IHod[] hod = { null, new IHod() { public void zobraz() { /* 1 new IHod() { public void zobraz() { /* 2 new IHod() { public void zobraz() { /* 3 new IHod() { public void zobraz() { /* 4 new IHod() { public void zobraz() { /* 5 new IHod() { public void zobraz() { /* 6 }; */ */ */ */ */ */ }, }, }, }, }, }, /********************************************** * Simuluje hod čísla, které následně zobrazí * číslem a vypodobněním horní strany kostky. */ public void hoď() { int číslo = RND.nextInt( 6 ) + 1; System.out.println( "Padlo číslo: " + číslo ); hod[ číslo ].zobraz(); } Copyright © 2009, Rudolf Pecinovský ICZ 109 Vaše jistota na trhu IT Obsah Iterátor (Iterator) ► Zprostředkuje jednoduchý a přehledný způsob sekvenčního přístupu k objektům uloženým v nějaké složité struktuře (většinou v kontejneru), přičemž implementace této struktury zůstane klientovi skryta. 033 Motivace ► Instance, které ukládáme do kontejneru, neodkládáme jako do popelnice – budeme je chtít v budoucnu použít ► Kontejner nás však nemůže nechat se ve svých útrobách přehrabovat – to by nám musel prozradit svou implementaci ► Potřebujeme mít možnost se kdykoliv dostat k uloženým datům, aniž by kontejner byl nucen cokoliv prozradit o své implementaci ► Problém řeší aplikace návrhového vzoru Iterátor Copyright © 2009, Rudolf Pecinovský ICZ 111 Charakteristika ► Skryje způsob uložení objektů v kontejneru a přitom umožní procházet kontejnerem a prácovat s uloženými prvky ► Sekvenční (externí) iterátor ● Na požádání předává externímu žadateli jednotlivé uložené objekty ● Vlastní akci iniciuje a zabezpečuje klient ► Dávkový (interní) iterátor ● Převezme od klienta filtr specifikující ošetřované prvky a příkaz, který se má na každý ošetřovaný prvek aplikovat ● Sám prochází prvky a na ty, které projdou filtrem, aplikuje obdržený příkaz Copyright © 2009, Rudolf Pecinovský ICZ 112 Princip externího iterátoru ► Kontejner definuje speciální třídu – iterátor, jejíž instance ví, jak jsou svěřená data uložena ► Tomu, kdo chce pracovat s uloženými daty vrátí kontejner na požádání instanci iterátoru, jenž mu přístup k uloženým datům zprostředkuje ► Instance iterátoru na požádání vrátí odkaz na další z instancí uložených v kontejneru ► Až iterátor všechna data vyčerpá, oznámí, že už další nejsou ► Tazatel se tak dostane ke všem uloženým datům, aniž by se dozvěděl, jak jsou vlastně uložena Copyright © 2009, Rudolf Pecinovský ICZ 113 Implementace ► Iterátor bývá v Javě implementován jako vnitřní třída ► Rozhraní Iterator<E> ze standardní knihovny vyžaduje implementaci metod: ● boolean hasNext() ● E next() ● void remove() (implementace může být formální) ► Seznamy umí vrátit také ListIterator, který přidává ● Možnost vložení nového prvku před či za aktuální ● Změnu směru průchodu seznamem ● Nahrazení naposledy vráceného prvku jiným ● Vrácení indexu předchozího, resp. následujícího prvku Copyright © 2009, Rudolf Pecinovský ICZ 114 interface Iterator package java.util; public interface Iterator<E> { /** Je ještě nějaká instance k dispozici? */ boolean hasNext(); /** Vrátí odkaz na další instanci. */ E next(); /** Odebere z kolekce * naposledy vrácenou instanci. * Metoda nemusí být plně implementována. */ void remove(); } Copyright © 2009, Rudolf Pecinovský ICZ 115 Ukázka použití: tisk kolekce s titulkem public static void println( String název, Collection<?> kolekce ) { System.out.print( název + ": (" ); String oddělovač = ""; for( Iterator<?> it = kolekce.iterator(); it.hasNext(); /* nic */ ) { Object o = it.next(); String s = o.toString(); System.out.print( oddělovač + s ); oddělovač = ", "; } System.out.println( ")" ); } Copyright © 2009, Rudolf Pecinovský ICZ 116 Starší implementace ► Java původně (tj. před verzí 1.2) definovala iterátor jako interface java.util.Enumeration s metodami ● boolean hasMoreElements() ● Object nextElement() ► V Java ME zůstává tato definice jako jediná Copyright © 2009, Rudolf Pecinovský ICZ 117 Iterovatelné objekty ► Java 5 zavedla iterovatelné objekty – jejich třídy implementují rozhraní Iterable<E> ► Rozhraní požaduje implementaci jediné metody: public Iterator<E> iterator() ► Jako iterovatelný objekt je možno definovat i … ● měřící zařízení, které vrací nekonečný sled měření ● generátor objektů (např. náhodných či testovacích) ● vstupní soubor ●… ► Zařazením objektu mezi iterovatelné získáme možnost využít knihovních metod, které s pracují s iterovatelnými objekty + „dvojtečkové“ verze cyklu for Copyright © 2009, Rudolf Pecinovský ICZ 118 Tisk kolekce s novou verzí cyklu public static void println( String název, Collection<?> kolekce ) { System.out.print( název + ": (" ); String oddělovač = ""; for( Object o : kolekce ) { //Objekt již mám přiřazen String s = o.toString(); System.out.print( oddělovač + s ); oddělovač = ", "; } System.out.println( ")" ); } Copyright © 2009, Rudolf Pecinovský ICZ 119 Příklad: Fronta – atributy a metody public class Fronta<E> implements Iterable<E> { private final List<E> prvky = new LinkedList<E>(); public void zařaď( E e ) { prvky.add( e ); } public E další() { if( prvky.size() == 0 ) return null; E ret = prvky.get(0); prvky.remove(0); return ret; } public Iterator<E> iterator() { return new MůjIterator( this ); } Copyright © 2009, Rudolf Pecinovský ICZ 120 Příklad: Fronta – vnořená třída private class MůjIterator implements Iterator<E> { private int pořadí = 0; private Fronta<E> f; MůjIterator( Fronta<E> fronta ) { f = fronta; } public boolean hasNext() { return (pořadí < f.prvky.size()); } public E next() { return f.prvky.get( pořadí++ ); } } Protože je definována uvnitř třídy, má přístup k soukromým složkám jejích instancí public void remove() { throw new UnsupporteOperationException(); } Copyright © 2009, Rudolf Pecinovský Neplnohodnotná implementace – volání metody způsobí chybu ICZ 121 Příklad: Fronta – Test public static void test() { Random rnd = new Random(); Fronta<Integer> fronta = new Fronta<Integer>(); System.out.println("===== Začátek testu ====="); System.out.println("Přidáváme:"); for( int i=0; i < 5; i++ ) { Integer číslo = new Integer(rnd.nextInt(100)); fronta.zařaď( číslo ); System.out.print("Přidáno: " + číslo); System.out.print (" Stav:"); //Použití cyklu for(:) na instance třídy Fronta for( Integer fi : fronta ) System.out.print( " " + fi ); System.out.println(""); } System.out.println("\nOdstraňujeme:"); for(;;) { Integer další = fronta.další(); Copyright © 2009, Rudolf Pecinovský ICZ 122 Příklad: iterátor složkou public interface IPříkaz { public void příkaz( Object... objects ); } public class Složka implements Iterable<File> { public Složka( String složka ) {/*tělo*/} public Složka( File složka ) {/*tělo*/} public public public public public Dávkový iterátor Návrhový vzor Příkaz void aplikuj( IPříkaz příkaz ) {/*tělo*/} void setFilter( FileFilter filtr ) { this.filtr = filtr; } void setComparator( Comparator<File> comparator ) {/*tělo*/} String toString() {/*tělo*/} Iterator<File> iterator() {return new FileIterator(filtr,comp);} private class FileIterator implements Iterator<File> { public FileIterator(FileFilter filtr, Comparator<File> komp) {/*tělo*/} public boolean hasNext() {/*tělo*/} public File next() {/*tělo*/} private File dalšíSoubor() {/*tělo*/} } } Copyright © 2009, Rudolf Pecinovský Iterátor ICZ 123 Rozhodnutí při definici iterátoru ► Kdo řídí iteraci: klient × kontejner (externí × interní) ● Externí iterátor je tvárnější (lze např. porovnat 2 kolekce), interní iterátor umožňuje zabezpečit větší robustnost ► Míru robustnosti iterátoru ● Jak citlivý bude na změnu struktury kontejneru v průběhu iterace ► Jakou množinu operací bude poskytovat ● Viz Enumeration × Iterator × ListIterator Copyright © 2009, Rudolf Pecinovský ICZ 124 Vaše jistota na trhu IT Obsah Stav (State) ► Řeší výrazný rozdíl mezi chováním objektu v různých stavech zavedením vnitřního stavu jako objektu reprezentovaného instancí některé ze stavových tříd. Změnu stavu objektu pak řeší záměnou objektu reprezentujícího stav. 034 Motivace ► Chování objektu se výrazně liší v závislosti na stavu, v němž se právě nachází ► Při klasickém přístupu bylo třeba v každé stavově závislé metodě definovat rozhodovací sekvenci s popisy příslušné reakci v každé větvi ► Nevýhody: ● Kód je dlouhý a nepřehledný ● Zavádění nových stavů je obtížné ● Při modifikaci reakce v konkrétním stavu je třeba najít příslušné pasáže v záplavě okolního kódu Copyright © 2009, Rudolf Pecinovský ICZ 126 Implementace ► Definice objektu se rozdělí na dvě části: ● Stavově nezávislou obsahující metody nezávisející na stavu ● Stavově závislou s metodami obsahujícími rozhodovací sekvence definující reakce se v závislosti na stavu ► Definuje se rozhraní (interface nebo abstraktní třída) deklarující metody s reakcemi závisejícími na stavu ► Pro každý stav se zavede samostatná třída implementující dané jednostavové rozhraní a definující chování objektu nacházejícího se v daném stavu ► Multistavový objekt definuje atribut odkazující na jednostavový objekt, na nějž pak deleguje reakce na zprávy závisející na aktuálním stavu Copyright © 2009, Rudolf Pecinovský ICZ 127 Příklad: rup.česky.vzory._17_stav.autoa ► Použití abstraktní rodičovské třídy umožňuje: ● „Vytknout“ společné atributy ● Definovat na jednom místě metody pro přechody mezi stavy ● Definovat tovární metodu vracející instanci požadovaného stavu Copyright © 2009, Rudolf Pecinovský ICZ 128 Vaše jistota na trhu IT Obsah Šablonová metoda (Template Method) 035 ► Definuje metodu obsahující kostru nějakého algoritmu. Ne všechny kroky tohoto algoritmu jsou však v době vzniku šablony známy – jejich konkrétní náplň definují až potomci třídy se šablonovou metodou prostřednictvím překrytí metod, které šablonová metoda volá. Charakteristika ► Návrhový vzor používaný i v učebnicích a kurzech, které se o návrhových vzorech vůbec nezmiňují ► Umožňuje podtřídám měnit části algoritmu bez změny samotného algoritmu ► Používá se při řešení typických úloh, jejichž přesné parametry budou známy až za běhu ► Umožňuje definovat metody, jejichž chování je definováno jen částečně; tyto části chování definují až potomci ► Jeden ze způsobů odstranění duplicit v kódu ► Takto bývají definovány klíčové objekty knihoven a rámců ● Aplety ● MIDlety Copyright © 2009, Rudolf Pecinovský ICZ 130 Implementace ► Definuje metodu obsahující kostru nějakého algoritmu, u nějž však v době konstrukce ještě nejsou všechny jeho kroky známy ► Konkrétní náplň neznámých kroků definují až potomci na základě svých speciálních dodatečných znalostí ► Třída se šablonovou metodou definuje příslušnou kostru a pro dosud neznámé postupy definuje virtuální metody, které potomci překryjí svými vlastními ● Je-li jedno zmožných řešení např. „nedělat nic“, je možno definovat virtuální metodu jako prázdnou ● Neexistuje-li žádné přijatelné implicitní řešení, definuje se metoda jako abstraktní ► Při aplikaci vzoru bývá často použit také návrhový vzor Příkaz Copyright © 2009, Rudolf Pecinovský ICZ 131 Template method – Velká a malá ryba ► Scénář: „Velké“ a „Malé“ ryby plavou oceánem ● ryby se pohybují náhodně ● velká ryba může plavat tam kde je malá (a sníst ji) ● malá ryba nemůže plavat tam kde je velká public void move() { vyber náhodný směr; najdi místo vtom směru; //odlišné pro druh ryby ověř lze-li tam plout; jestli ano, pluj; } Copyright © 2009, Rudolf Pecinovský ICZ 132 Příklad: Aplety public class Applet extends Panel { public Applet() throws HeadlessException { public final void setStub(AppletStub stub) { public boolean isActive() { public URL getDocumentBase() { public URL getCodeBase() { public String getParameter(String name) { public AppletContext getAppletContext() { public void resize(int width, int height) { public void resize(Dimension d) { public void showStatus(String msg) { public Image getImage(URL url) { public Image getImage(URL url, String name) { public final static AudioClip newAudioClip(URL url) { public AudioClip getAudioClip(URL url) { public AudioClip getAudioClip(URL url, String name) { public String getAppletInfo() { public Locale getLocale() { public String[][] getParameterInfo() { public void play(URL url) { public void play(URL url, String name) { } public public public public void void void void init() { start() { stop() { destroy() { Copyright © 2009, Rudolf Pecinovský ICZ 133 Příklad: MIDlety Start konstruktor startApp Čekající startApp pauseApp Aktivní destroyApp destroyApp Ukončený Copyright © 2009, Rudolf Pecinovský ICZ 134 Příklad: DieMIDlet_1 – realizace import javax.microedition.midlet.*; public class DieMIDlet_1 extends MIDlet { private static int m = 0; public DieMIDlet_1() { System.out.println("Midlet_1 Nr. "+ ++m +" constructed"); }// DieMIDlet_1() public void startApp() { System.out.println("Midlet_1 Nr. " + m + " activated"); new DieCast_1(this); //DieCast starts another thread }//startApp() public void pauseApp() {} public void destroyApp( boolean unconditional ) throws MIDletStateChangeException { System.out.println("Midlet_1 Nr. " + m + " destroyed"); notifyDestroyed(); }//public void destroyApp( boolean unconditional ) }//class DieMIDlet_1 extends MIDlet Copyright © 2009, Rudolf Pecinovský ICZ 135 Vaše jistota na trhu IT 04 Obsah Optimalizace rozhraní ► Fasáda (Facade) 041 ► Adaptér (Adapter) 042 ► Strom (Composite) 043 04 Vaše jistota na trhu IT Obsah Fasáda (Facade) 041 ► Ukazuje jak nahradit sadu rozhraní jednotlivých subsystémů sjednoceným rozhraním zastupujícím celý systém. Definuje tak rozhraní vyšší úrovně, které usnadní využívání podsystémů. Jejím cílem je zjednodušit rozhraní celého systému a snížit počet tříd, s nimiž musí uživatel přímo či nepřímo komunikovat. Charakteristika ► Použijeme jej ve chvíli, kdy nějaký systém začíná být pro své uživatele příliš složitý vzhledem k oblasti úloh, které chtějí s jeho pomocí řešit ► Z celého spektra dostupných metod vybere podmnožinu nejpoužívanějších, nebo přímo definuje vzorové verze metod pro nejčastěji používané úlohy ► Možné způsoby implementace ● Rozhraní či abstraktní třídy (příp. systém R+AT) jejichž implementaci definují až potomci ● Konfigurovatelná třída (slabší varianta předchozího) ● Samostatná třída (skupina tříd) poskytující nejčastěji požadované metody Copyright © 2009, Rudolf Pecinovský ICZ 138 Komunikace tříd před a po použití fasády ► Příklad: ● javax.swing.JOptionPane Copyright © 2009, Rudolf Pecinovský ICZ 139 Výhody použití ► Redukuje počet objektů, s nimiž klienti komunikují ● Snadnější použití subsystému ► Zmenšuje počet závislostí mezi klienty a subsystémem ● Odstraňuje některé komplexní a kruhové závislosti ● Méně závislostí při překladu i při běhu ► Liberální fasáda: neskrývá třídy subsystému ● Klient si může vybrat jednoduchost nebo použití na míru ► Přísná fasáda: nezaručuje implementaci systému ● Náhražka neexistující možnosti přísnějšího skrytí implementace ● Java 7 má zavést superpackages – pak nebude potřeba Copyright © 2009, Rudolf Pecinovský ICZ 140 Vaše jistota na trhu IT Obsah Adaptér (Adapter) 042 ► Návrhový vzor Adaptér využijeme ve chvíli, kdy bychom potřebovali, aby třída měla jiné rozhraní, než to, které právě má. Pak mezi ní a potenciálního uživatele vložíme třídu adaptéru, která bude mít požadované rozhraní a konvertuje tak rozhraní naší třídy na rozhraní požadované. Motivace ► Občas potřebujeme, aby třída měla jiné rozhraní než to, které má ● Třída neimplementuje požadované rozhraní, nicméně poskytuje požadovanou funkčnost ● Příklad: používáme třídu z jedné knihovny, jejíž instance bychom mohli použít jako parametry metod jiné knihovny, ale tato jiná knihovna vyžaduje parametry implementující nějaké specifické rozhraní, které naše třída nezná ► Dopředu víme, že z hlediska požadované funkčnosti stačí implementovat pouze část daného rozhraní nicméně překladač vyžaduje kompletní implementaci ● Iterátor neumožňující odstraňovat prvky z kontejneru ● Kontejnery s předem zadaným, nezměnitelným obsahem ● Posluchači některých událostí při tvorbě GUI Copyright © 2009, Rudolf Pecinovský ICZ 142 Charakteristika ► Účel ● ● Zabezpečit spolupráci již exitujících tříd, tj. tříd, jejichž rozhraní už nemůžeme měnit Usnadnit definici nových tříd, v nichž pak nemusíme implementovat celé požadované rozhraní ► Nasazení ● Použití tříd z pevných knihoven v jiných prostředích ● Využití třídy s požadovanou funkčností, ale jiným rozhraním ● Implementace ekvivalence mezi rozhraními ● Doplnění funkcionality třídy na požadovanou rozhraním Copyright © 2009, Rudolf Pecinovský ICZ 143 Adaptér jako rodič adaptované třídy ► Třída Adaptér definuje implicitní implementace všech metod požadovaných rozhraním IPožadované přičemž implicitní verze typicky: ● Vyhazuje UnsupportedOperationException ● Nedělá nic ► Potomci pak mohou definovat pouze ty metody, které se jim „hodí do krámu“ ► Pro klienta potomek implementuje vše Copyright © 2009, Rudolf Pecinovský ICZ 144 Příklad public interface IPosuvný extends IKreslený { //== DEKLAROVANÉ METODY ================================= Adaptér je definován jako public Pozice getPozice(); třída vnořená do rozhraní, public void setPozice( Pozice pozice ); na něž bude své potomky public void setPozice( int x, int y ); adaptovat //== VNOŘENÉ TŘÍDY ====================================== public static class Adaptér extends IKreslený.Adaptér implements IPosuvný { public Pozice getPozice(){ throw new UnsupportedOperationException(); } public void setPozice( int x, int y ) { throw new UnsupportedOperationException(); } public void setPozice( Pozice pozice ) { setPozice( pozice.x, pozice.y ); } } Metody, o jejichž implementaci se uživatel může rozhodnout podle potřeby Metoda s definovatelnou implementací používající vzor Šablonová metoda } Copyright © 2009, Rudolf Pecinovský ICZ 145 Možná implementace adaptéru – předka public interface IPosuvný extends IKreslený { //== DEKLAROVANÉ METODY ================================= public Pozice getPozice(); Adaptér je definován jako vnořená třída rozhraní, public void setPozice( Pozice pozice ); na něž bude své potomky public void setPozice( int x, int y ); adaptovat //== VNOŘENÉ TŘÍDY ====================================== public static class Adaptér extends IKreslený.Adaptér implements IPosuvný { public Pozice getPozice(){ throw new UnsupportedOperationException(); Metoda s definovatelnou } implementací používající public void setPozice( Pozice pozice ){ setPozice( pozice.x, pozice.y ); } public void setPozice( int x, int y ) { throw new UnsupportedOperationException(); } vzor Šablonová metoda Metody, o jejichž implementaci se uživatel může rozhodnout podle potřeby } } Copyright © 2009, Rudolf Pecinovský ICZ 146 Adaptovaný objekt jako atribut 1/2 ► Pracuje stejně jako ochranný zástupce, pouze s jinou motivací ► Adaptér definuje atribut s odkazem na adaptovaný objekt public class Adaptér implements IPožadované { Existující adaptovaný; public Adaptér(Existující exist) { adaptovaný = exist; } Copyright © 2009, Rudolf Pecinovský ICZ 147 Adaptovaný objekt jako atribut 2/2 ► Všechna volání metod „přehrává“ na volání ekvivalentních metod adaptovaného objektu public class Adaptér implements IPožadované { Existující adaptovaný; public Adaptér(Existující exist) { adaptovaný = exist; } public void metoda(Parametr parametr) { Požadovaný požadovaný = uprav(parametr); adaptovaný.jehoMetoda(požadovaný); } } Copyright © 2009, Rudolf Pecinovský ICZ 148 Příklady ► Adaptace prostřednictvím předka ● Všechna rozhraní posluchačů java.awt.event.XyzListener deklarující více než jednu metodu mají sdružené třídy java.awt.event.XyzAdapter ► Adaptace prostřednictvím atributu ● Instance třídy Barva z knihovny Tvary jsou jemně upravenými počeštěnými obálkami kolem instance typu java.awt.Color ● Instance třídy Kreslítko z knihovny Tvary jsou zestručněnými počeštěnými obálkami kolem instance typu java.awt.Graphics2D Copyright © 2009, Rudolf Pecinovský ICZ 149 Vaše jistota na trhu IT Obsah Strom (Composite) ► Sjednocuje typy používaných objektů a umožňuje tak jednotné zpracování každého z nich nezávisle na tom, jedná-li se o atomický (tj. dále nedělitelný) objekt nebo o objekt složený z jiných objektů. 043 Charakteristika ► Ukazuje, jak vytvořit hierarchii složenou ze dvou druhů objektů: ● Atomických (primitivních) ● Složených (z atomických či dalších složených) ► Většinou tvoří struktura strom, ale není to nutné ► Použití ● Grafické editory vytvářející složité objekty z jednodušších ● Reprezentace tahů a protitahů ve hrách ● Adresářová struktura ● Struktura různých orgranizací ● Implementace nejrůznějších dalších stromových struktur Copyright © 2009, Rudolf Pecinovský ICZ 151 Diagram tříd v návrhovém vzoru Strom ► Doporučuje definovat pro atomické i složené objekty společného rodiče Copyright © 2009, Rudolf Pecinovský ICZ 152 Důsledky použití vzoru ► Zjednodušuje klienta, protože může s atomickými i složenými objekty zacházet jednotně ► Jednodušeji se přidávají nové komponenty ● Díky společnému rodiči je klient automaticky zná ● Je-li rodičem abstraktní třída, může v ní být již řada potřebných metod definována ► Nebezpečí ● Jednoduché přidávání komponent komplikuje verifikaci, že přidávaná komponenta smí být přidána – je třeba použít kontroly za běhu ● Metody ve společném rodiči zvyšují obecnost, ale současně snižují robustnost (nemusí na konkrétního potomka sedět), metody v potomcích naopak Copyright © 2009, Rudolf Pecinovský ICZ 153 Vaše jistota na trhu IT 05 Otevřenost variantním řešením 05 Obsah ► Tovární metoda (Factory method) 051 ► Prototyp (Prototype) 052 ► Stavitel (Builder) 053 ► Abstraktní továrna (Abstract factory) 054 Společný příklad: GoF/Xiaoping: Bludiště ► Bludiště, v němž jsou jednotlivé místnosti ohraničeny stěnami, dveřmi či sousedními místnostmi; každá místnost zná své sousedy na hranicích ► Všechny uvedené objekty jsou potomky součástí bludiště ► Zdroj: http://se.cs.depaul.edu/Java/chap10.html Copyright © 2009, Rudolf Pecinovský ICZ 155 Vaše jistota na trhu IT Obsah Tovární metoda (Factory method) ► Deklaruje rozhraní s metodou pro získání objektu. Rozhodnutí o konkrétním typu vráceného objektu však ponechává na svých potomcích, tj. na překrývajících verzích deklarované metody. 051 Charakteristika ► Oproti jednoduché tovární metodě bývá standardní tovární metoda definována jako abstraktní metoda společného předka, tj. definuje se pouze, že potomci definují metodu vracející objekty daného typu ► Skutečnou podobu metody a tím i to, co přesně bude metoda vracet, definují až potomci ► Potomci definují nejenom vlastnosti vytvářeného objektu, ale především jeho skutečný typ – občas bývá označována jako virtuální konstruktor ► Umožňuje oddělení proměnného a neměnného kódu ► Typické příklady: Iterator, Graphics Copyright © 2009, Rudolf Pecinovský ICZ 157 Diagram tříd vzoru Tovární metoda Copyright © 2009, Rudolf Pecinovský ICZ 158 Aplikace tovární metody v knihovně kontejnerů Copyright © 2009, Rudolf Pecinovský ICZ 159 Kolekce – továrny na iterátory Containers in java.util package in Java 1.4 Collection Set SortedSet Map AbstractCollection AbstractSet List AbstractList HashSet Dictionary ListIterator HashTable RandomAccess ArrayList TreeSet Iterator Enumeration Vector AbstractSequentialList AbstractMap SortedMap HashMap TreeMap Properties IdentityHashMap WeakHashMap Cooperation LinkedHashMap Comparator LinkedHashSet LinkedList Stack Arrays Interface AbstractClass ConcreteClass Copyright © 2009, Rudolf Pecinovský Comparable Version 1.02 03-05-21 ICZ OlderInterface Collections OlderAbstractClass extends implements produces OlderConcreteClass 160 Vlastnosti ► Umožňuje ignorovat implementaci a soustředit se na rozhraní, tj. na klíčové vlastnosti objektu ► Zapouzdřuje vytváření instancí; při jejím použití se není třeba starat o to, jak použitá instance vznikne ► Umožňuje, aby aplikace byla schopna vytvářet instance i od tříd, které ještě nezná ► Třída nemusí vždy tovární metodu definovat, často ji stačí převzít od předka ► Třídy, jejichž instance tovární metody vracejí, jsou často definovány jako interní třídy „továrních tříd“ ► Někdy tovární metody používají klonování – viz dále Copyright © 2009, Rudolf Pecinovský ICZ 161 Příklad GoF/Xiaoping: Bludiště Copyright © 2009, Rudolf Pecinovský ICZ 162 Příklad GoF/Xiaoping: Bludiště src1/2 public class HarryPotterMazeGameCreator extends MazeGameCreator { public Wall makeMaze() { return new HarryPotterMaze(); } public Wall makeWall() { return new HarryPotterWall(); } public Room makeRoom(int roomNumber) { return new HarryPotterRoom(roomNumber); } public Door makeDoor(Room room1, Room room2) { return new HarryPotterDoor(room1, room2); } } Copyright © 2009, Rudolf Pecinovský ICZ 163 Příklad GoF/Xiaoping: Bludiště src2/2 public class MazeGameCreator { public Maze createMaze() { Maze maze = makeMaze(); Room room1 = makeRoom(1); //... Door door1 = makeDoor(room1, room2); //... door1.setOpen(true); //... room1.setSide(Direction.NORTH, door8; //... Implicitní maze.addRoom(room1); ; definice mohou být nahrazeny //... abstraktními return maze; } public Maze makeMaze() { return new Maze(); } public Wall makeWall() { return new Wall(); } public Room makeRoom( int roomNumber) { /*...*/ } public Door makeDoor(Room room1, Room room2) {/*...*/} Copyright © 2009, Rudolf Pecinovský ICZ 164 Vaše jistota na trhu IT Obsah Prototyp (Prototype) ► Objekt definuje tovární metodu, která bude vytvářet jeho kopie. V programu je pak možno místo přímého volání konstruktoru využívat volání kopírovací tovární metody připraveného prototypu. 052 Tři cesty k vytvoření objektu ► Konstruktor ● Jediná cesta, jak vytvořit skutečně novou instance ► Klonování ● Vytvoření kopie nějaké dříve vytvořené instance ► Serializace – deserializace ● Načtení kopie nějaké dříve vytvořené instance z nějakého úložiště Copyright © 2009, Rudolf Pecinovský ICZ 166 Diagram tříd návrhového vzoru Prototyp ► Můžeme definovat i vlastní mechanizmus klonování, ale využití podpory systému (implementace rozhraní Cloneable) je většinou výhodnější Copyright © 2009, Rudolf Pecinovský ICZ 167 Implementace klonování v Javě 1/2 ► Klonovací metoda nebývá většinou obálka kolem konstruktoru, ale metoda řešící kopírování vzorové instance na systémové úrovni ► Metodu clone() definuje třída Object, od které ji všichni dědí ► Systémová verze metody je ochotna „zkopírovat“ pouze instanci třídy implementující rozhraní Cloneable ► Systémová verze je definována jako protected, takže ji bez zveřejnění nemůže použít kdokoliv Copyright © 2009, Rudolf Pecinovský ICZ 168 Implementace klonování v Javě 2/2 ► Typická podoba zveřejňovaci definice: public Object clone() { return super.clone(); } ► Dokud třída neimplementuje Cloneable, systémová definice se s ní nebaví ► Třída nemusí používat super.clone(), může v případě potřeby definovat vlastní verzi definovanou jako obálku kopírovacího konstruktoru ► Nepotřebuje-li třída super.clone(), nemusí implementovat Cloneable Copyright © 2009, Rudolf Pecinovský ICZ 169 Duch definice ve třídě Object public Object clone() { if (!(this instanceof Cloneable)) { throw new CloneNotSupportedException(); } //Vyhradí místo v paměti Object clone = allocateNew( this ); //Zkopíruje do něj obsah instance byteCopy( this, clone ); return clone; } Copyright © 2009, Rudolf Pecinovský ICZ 170 CloneNotSupportedException ► Aby nebylo v programech třeba ošetřovat možný výskyt kontrolované výjimky CloneNotSupportedException, lze po objektech požadovat, aby implementovali rozhraní, v němž je metoda clone() deklarována jako metoda nevyhazující žádnou kontrolovanou výjimku ► Používáme-li instance knihovních tříd, které takovéto rozhraní neimplementují, můžeme je přizpůsobit našim požadavkům prostřednictvím adaptéru Copyright © 2009, Rudolf Pecinovský ICZ 171 Mělká a hluboká kopie ► Mělká kopie přebírá od originálu jeho odkazy, tj. kopie odkazuje na ty samé objekty, jako originál ► Hluboká kopie definuje také kopie odkazovaných objektů ► Odkazované objekty mohou být opět zkopírovány mělce či hluboce ► To, zda bude objekt kopírován mělce či hluboce, si objekt definuje sám ► Systémová verze kopíruje mělce Copyright © 2009, Rudolf Pecinovský ICZ 172 Kdy co ► Obecně bývá výhodnější ● Využití metody clone() při používání mělkých kopií ● Využití speciální tovární metody volající konstruktor při používání hlubokých kopií ► Nemusí tomu tak ale být vždy ► Návrhový vzor Prototyp není fixován na metodu clone(); místo ní můžeme pro vytváření kopií definovat i vlastní metodu Copyright © 2009, Rudolf Pecinovský ICZ 173 Doporučené invarianty ► Klon nemusí být povinně kopie, měly by však dodržet následující invarianty: x.clone() != x x.clone().equals(x) x.clone().getClass() == x.getClass() ► Teoreticky je možno definovat metodu , která tyto invarianty nedodržuje, ale musí pro to být nějaký pádný důvod a je třeba zabezpečit, aby s tím zbytek programu počítal Copyright © 2009, Rudolf Pecinovský ICZ 174 Kopírování instancí hodnotových typů ► Je např. možno využít skutečnosti, že nás nezajímá skutečná kopie, ale kopie hodnoty – pak lze definovat: public Object clone() { return this; } ► Platí pouze pro neměnné typy ► Porušuje doporučen invariant x.clone() != x, ale u hodnotových typů se většinou na rozdíly instancí nehledí Copyright © 2009, Rudolf Pecinovský ICZ 175 Další vlastnosti klonování ► Klonovací metoda se chová jako zvláštní tovární metoda, kde tovární třídu zastupuje klonovaná instance ► Umožňuje, aby aplikace byla schopna vytvářet instance i od tříd, které ještě nezná, výrazně efektivněji než při použití reflexe ► Klonování umožňuje definovat i pseudotřídy tvořené skupinami instancí stejné třídy ● Ve virtuálním světe mám objekty definované jako mnohotvary, jejichž instance se mohou sdružovat do pseudotříd: osoba – auto – autobus ● Takovéto pseudotřídy mohou vnikat dynamicky za běhu Copyright © 2009, Rudolf Pecinovský ICZ 176 Použití v praxi ► Vracení interního pole či jiného kontejneru ● Vrácením klonu zabezpečíme, že žadatel nemá šanci obsah pole změnit ► Zkrácení seznamu parametrů ● Mám-li definovanou instanci, která může sloužit jako vzor, nemusím při vytváření jejích kopií předávat parametry Copyright © 2009, Rudolf Pecinovský ICZ 177 Příklad GoF/Xiaoping: Bludiště GoTo FactoryMethod Copyright © 2009, Rudolf Pecinovský ICZ 178 Příklad GoF/Xiaoping: Bludiště src public class MazePrototypeFactory extends MazeFactory { public MazePrototypeFactory( Maze mazePrototype, Wall wallPrototype, Room roomPrototype, Door doorPrototype) { this.mazePrototype = mazePrototype; this.wallPrototype = wallPrototype; this.roomPrototype = roomPrototype; this.doorPrototype = doorPrototype; } public Maze makeMaze() { try { return (Maze) mazePrototype.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException( e ); } } Copyright © 2009, Rudolf Pecinovský ICZ 179 Vaše jistota na trhu IT Obsah Stavitel (Builder) 053 ► Odděluje konstrukci složitého objektu (tj. postup jeho tvorby) od jeho vnitřní reprezentace. Tím umožňuje využít stejného konstrukčního postupu pro různé vnitřní reprezentace konstruovaných objektů. Charakteristika ► Účelem vzoru Stavitel je umožnit využití stejného konstrukčního postupu pro různé vnitřní reprezentace konstruovaných objektů ► Návrhový vzor stavitel se nesoustřeďuje na vlastní vytvoření a „dodání“ objektu, ale na postup jeho tvorby Copyright © 2009, Rudolf Pecinovský ICZ 181 Řídící objekt × výkonné objekty ► V návrhovém vzoru vystupuje vedle sebe řídící objekt a výkonné objekty ► Řídící objekt má na starosti dodržení správného postupu vytvoření požadovaného objektu ► Výkonné objekty mají na starosti vytvoření některých detailů podle pokynů řídícího objektu ► V některých případech jsou výkonné objekty schopny vytvořit podle návodu řídícího objektu celý požadovaný objekt Copyright © 2009, Rudolf Pecinovský ICZ 182 Diagram tříd návrhového vzoru Stavitel Copyright © 2009, Rudolf Pecinovský ICZ 183 Výkonné objekty ► Pro různé druhy vytvářených objektů (např. pro jejich různé vnitřní reprezentace) se používají různé výkonné objekty, tj. objekty, které jsou instancemi různých tříd ► Aby mohly být výkonné objekty jednotně používány, implementují společné rozhraní, případně mají společného rodiče ► Každý výkonný objekt je typicky schopen vytvořit kteroukoliv z částí požadovaných řídícím objektem. Jednotlivé výkonné objekty se liší pouze v tom, jak tuto část vytvoří Copyright © 2009, Rudolf Pecinovský ICZ 184 Použití: ► Je-li algoritmus tvorby objektu nezávislý na použité vnitřní reprezentaci ► Mají-li vytvářené objekty složitější vnitřní strukturu a budují-li se proto v několika fázích ► Je-li vhodné použít řídící objekt současně jako správce zdrojů používaných při tvorbě daných objektů Copyright © 2009, Rudolf Pecinovský ICZ 185 Příklad: Sázecí program package rup.česky.vzory._24_stavitel.text; public interface ISázecíStroj //Výkonný { public String běžný ( String text public String tučný ( String text public String šikmý ( String text public String odstavec( String text public String dokument( String text } Copyright © 2009, Rudolf Pecinovský ICZ ); ); ); ); ); 186 Sázecí program – diagram tříd Autor vyrobí text složený z odstavců a předá jej sazeči spolu se sázecím strojem, jehož prostřednictvím chce text vysadit Výkonný objekt Výkonný objekt Výkonný objekt Řídící objekt Ukázka Copyright © 2009, Rudolf Pecinovský ICZ 187 Příklad GoF/Xiaoping: Bludiště GoTo FactoryMethod Výkonný objekt GoTo Prototype Řídící objekt Copyright © 2009, Rudolf Pecinovský ICZ 188 Příklad GoF/Xiaoping: Bludiště src1/2 public interface MazeBuilder { public void newMaze(); public Maze getMaze(); public void buildRoom(int roomNumber); public void buildDoor(int roomNumber1, int roomNumber2, Direction dir, boolean open); } public class MazeGameBuilder //Director { public static Maze createMaze(MazeBuilder builder) { builder.newMaze(); builder.buildRoom(1); builder.buildRoom(2); //... builder.buildDoor(1, 2, Direction.WEST, true); builder.buildDoor(2, 3, Direction.WEST, false); //... } return builder.getMaze(); Copyright © 2009, Rudolf Pecinovský ICZ 189 Příklad GoF/Xiaoping: Bludiště src2/2 public class FactoryMazeBuilder implements MazeBuilder { public FactoryMazeBuilder(MazeFactory factory) { this.factory = factory; } public void newMaze() { maze = factory.makeMaze(); } public Maze getMaze() { return maze; } public void buildRoom(int roomNumber) { if (maze == null) newMaze(); Room room = factory.makeRoom(roomNumber); //... maze.addRoom(room); } public void buildDoor(int roomNumber1, int roomNumber2, Direction dir, boolean open) { //... } //... } Copyright © 2009, Rudolf Pecinovský ICZ 190 Vaše jistota na trhu IT Obsah Abstraktní továrna (Abstract factory) ► Definuje rozhraní pro tvorbu celé rodiny souvisejících nebo závislých objektů, a tím odděluje klienta od vlastního procesu vytváření objektů. 054 Charakteristika ► Návrhový vzor Abstraktní továrna řeší problém, kdy je třeba vybrat mezi několika sadami tříd, jejichž instance budou vystupovat v programu ► Typickým použitím Abstraktní továrny je volba celkového vzhledu a chování (look and feel) GUI ► Instance vracené jednotlivými metodami mohou být jak různého, tak stejného typu ► Někdy je označován jako Kit (souprava, stavebnice) Copyright © 2009, Rudolf Pecinovský ICZ 192 Schéma A1 +akceAa() +akceAb() +akceAc() Továrna1 Principiální schéma vzoru Abstraktní tovární metoda IA +getA() : IA +getB() : IB +getC() : IC +akceAa() +akceAb() +akceAc() B1 C1 +akceCa() +akceCb() +akceCc() +akceCd() AbstraktníTovárna +akceAa() +akceAb() IB +getA() : IA +getB() : IB +getC() : IC Copyright © 2009, Rudolf Pecinovský +akceBa() +akceBb() A2 +akceAa() +akceAb() +akceAc() Továrna2 +getA() : IA +getB() : IB +getC() : IC B2 C2 +akceCa() +akceCb() +akceCc() +akceCd() +akceAa() +akceAb() IC +akceCa() +akceCb() +akceCc() +akceCd() Klient ICZ 193 Implementace ► Při implementaci Abstraktní továrny se často používá vzor Tovární metoda ► Jednotlivé používané „tovární instance“ bývají definovány jako jedináčci ► Návrhový vzor Abstraktní továrna neumožňuje jednoduše přidávat další rozhraní, jejichž instance budou „tovární instance“ dodávat; řešení s univerzální tovární metodou snižuje robustnost systému ► Při implementaci Abstraktní továrny se často používá vzor Prototyp Copyright © 2009, Rudolf Pecinovský ICZ 194 Diagram tříd objektů virtuálního světa Copyright © 2009, Rudolf Pecinovský ICZ 195 Diagram tříd simulace virtuálního světa Copyright © 2009, Rudolf Pecinovský ICZ 196 Příklad GoF/Xiaoping: Bludiště GoTo FactoryMethod GoTo Prototype GoTo Builder Copyright © 2009, Rudolf Pecinovský ICZ 197 Příklad GoF/Xiaoping: Bludiště src public class MazeGameAbstractFactory { public static Maze createMaze(MazeFactory factory) { Maze maze = factory.makeMaze(); Room room1 Room room2 //... Door door1 Door door2 //... = factory.makeRoom(1); = factory.makeRoom(2); = factory.makeDoor(room1, room2); = factory.makeDoor(room2, room3); ► Konstruktor abstraktní továrny dostane konkrétní továrnu jako parametr a tu pak žádá o vytvoření jednotlivých objektů Copyright © 2009, Rudolf Pecinovský ICZ 198 Vaše jistota na trhu IT 06 Obsah Ochrana před rozrůstáním 06 ► Dekorátor (Decorator) 061 ► Řetěz odpovědnosti (Chain of responsibility) 062 ► Pozorovatel (Observer) 063 ► Prostředník (Mediator) 064 Vaše jistota na trhu IT Obsah Dekorátor (Decorator) 061 ► Přidává další přídavnou funkcionalitu k objektu tak, že objekt „zabalí“ do jiného objektu, který má na starosti pouze přidanou funkcionalitu a zbytek požadavků deleguje na „zabalený“ objekt. Tím umožňuje přidávat funkčnost dynamicky a zavádí flexibilní alternativu k dědění. Motivace ► V knihovně potřebujeme nabízet třídy‘ poskytující různé kombinace funkcionalit ► Při nutnosti nabízet téměř všechny možné kombinace roste počet potřebných tříd kombinatoricky = neúnosně ► S každou další přidanou funkcionalitou musíme přidat násobek dosud existujících tříd ► S každou přidanou základní třídou musíme přidat i třídy pro možné kombinace přidaných funkcionalit Copyright © 2009, Rudolf Pecinovský ICZ 201 Charakteristika ► Návrhový vzor Dekorátor umožňuje snížit kombinatoricky rostoucí počet tříd ► Každá dodaná funkčnost je implementována jako „ozdobení“ (dekorování) jiného objektu ► Dekorátor rozšiřuje (zdobí) objekt, ne třídu; na rozdíl od statické dědičnosti pracuje dynamicky ► Základní třídy i dekorátory mají společného rodiče ► Příklad ● Knihovna proudů java.io (s bufferem, řádkováním, daty, …) ● swing (pole, s posuvníkem, s okrajem, s oběma, …) Copyright © 2009, Rudolf Pecinovský ICZ 202 Implementace 1/2 ► Využít postupu aplikovaného u návrhového vzoru Adaptér a zabalit objekt do objektu poskytujícího novou funkcionalitu, který tak původní objekt ozdobí (dekoruje) novou funkcionalitou ► Pro každou novou funkcionalitu pak bude stačit přidat pouze jedinou obalovou (dekorující) třídu ► Konstruktor dekorátoru přebírá dekorovaný objekt jako parametr a „obalí“ jej vlastními schopnostmi ► Dekorátor většinou přidává metody, které se starají o jím přidanou funkčnost. Může ale také pouze překrýt metody svých předků a definovat je pro daný účel efektivněji ► Požadavky nesouvisející s dodatečnou funkčností předá dekorátor dekorovanému objektu a vrátí obdržený výsledek Copyright © 2009, Rudolf Pecinovský ICZ 203 Implementace 2/2 ► Přidávané funkcionality je možné kumulovat, tj. zabalený objekt lze znovu zabalit do objektu přidávajícího další funkcionalitu ● Výsledný objekt lze vytvářet postupně: AAuto benzin = new Benzin(); AAuto abs = new ABS( benzin ); AAuto auto = new Automat(abs); ● Nebo najednou AAuto auto1 = new Automat( new ABS(new Benzin()) ); AAuto auto2 = new Automat( new ABS( new Benzin()) ); ► Dekorátory mají někdy ještě vlastního rodiče se společnou implementací. ► Ten pak bývá koncipován jako adaptér nabízející implicitní funkčnost: delegování metod na obalený objekt a vracení obdržených výsledků Copyright © 2009, Rudolf Pecinovský ICZ 204 Struktura tříd při implementaci vzoru Dekorátor ► Dekorátor pracuje jako obálka kolem dekorovaného objektu zabezpečující dodatečnou funkčnost ► Občas bývá nazýván Wrapper Copyright © 2009, Rudolf Pecinovský ICZ 205 Využití adaptéru ► Pro všechny dekorující třídy lze definovat společného rodiče, který volání neupravených metod převede na volání odpovídajících metod dekorovaného objektu ► Každá nově přidaná základní třída automaticky získává všechny v úvahu přicházející funkcionality Copyright © 2009, Rudolf Pecinovský ICZ 206 Společný rodič dekorujících tříd public class Dekorovaný extends AAuto { AAuto dekorovaný; public Dekorovaný(AAuto auto) { dekorovaný = auto; } public void zrychliNa(int rychlost, int doba) { dekorovaný.zrychliNa( rychlost, doba ); } public void zpomalNa(int rychlost, int dráha) { dekorovaný.zpomalNa( rychlost, dráha ); } public void zabrzdiNa(int dráha) { dekorovaný.zabrzdiNa( dráha ); } } Copyright © 2009, Rudolf Pecinovský ICZ 207 Koncepce čtení a zápisu dat v java.io Copyright © 2009, Rudolf Pecinovský ICZ 208 Příklady java.io.FilterInputStream java.io.FilterOutputStream java.io.FilterReader java.io.FilterWriter package rup.česky.vzory._26_dekorátor; ASCIIReader ASCIIWriter MultiWriter Copyright © 2009, Rudolf Pecinovský ICZ 209 +/– ► Výhody ● Pracuje dynamicky ● Lze je vrstvit jeden na druhý ● Není potřeba předvídat všechny potřeby klienta ► Nevýhody ● Dekorátor není dekorovaný objekt => prostřednictvím dekorátoru není možné volat metody, s nimiž dekorátor nepočítal ● Postupná delegace volání může mírně zpomalit kód ● Dekorovaný objekt je stále obecně dostupný => je třeba hlídat, kdy oslovuji vlastní objekt a kdy dekorovaný objekt Copyright © 2009, Rudolf Pecinovský ICZ 210 Vaše jistota na trhu IT Obsah Řetěz odpovědnosti (Chain of responsibility) 062 ► Umožňuje, aby zaslaný požadavek zpracoval jiný objekt než ten, kterému byl zadán. Doporučuje objekty zřetězit tak, aby objekt, který není schopen požadavek zpracovat, mohl požadavek předat dalšímu objektu v řetězu. Charakteristika ► Návrhový vzor Řetěz odpovědnosti ukazuje možné řešení úlohy, vyžadující zaslání zprávy některému ze skupiny objektů, aniž by bylo předem známo, který z nich je v daný okamžik ten pravý ► Využívá se v případě, kdy není dopředu jasné, koho oslovit ► Vzor minimalizuje provázanost mezi odesilatelem a příjemcem zprávy ► Umožňuje měnit řetěz dynamicky za chodu Copyright © 2009, Rudolf Pecinovský ICZ 212 Proč řetěz ODPOVĚDNOSTI ► Příjemci jsou ve frontě a zprávu si předávají, dokud ji někdo nezpracuje ► Instance, kterou oslovíme, je zodpovědná za to, že na zprávu správně zareaguje Ona ale tuto svoji odpovědnost přehraje na další instanci v řetězu navzájem na sebe napojených instancí Copyright © 2009, Rudolf Pecinovský ICZ 213 Aplikace ► Typické použití: GUI, kontextová nápověda ► Obecně ve stromových strukturách: když se nějakého uzlu na něco zeptáme a on nebude umět odpovědět, tak přehraje požadavek na svůj rodičovský uzel, nebude-li to vědět ani on, přehraje to opět na svého rodiče, a tak to poběží dál, až to v případě neschopnosti kohokoliv odpovědět skončí někde v koření celého stromu ► Může to být i obráceně: rodič se ptá svých potomků, neumí-li někdo na danou zprávu odpovědět; potomek opět může rekurzivně poslat dotaz dál Copyright © 2009, Rudolf Pecinovský ICZ 214 Implementace ► Skupina instancí, z nichž bude některá reagovat, je navzájem propojena odkazy do řetězu (seznamu) ► Neumí-li oslovený objekt reagovat, předá požadavek dalšímu v řetězu s nadějí, že on to dokáže ► Řetěz umožňuje dynamicky měnit konečného adresáta dané zprávy ► Instance v řetězu mohou být různých typů – pak je vhodné aplikovat vzor Strom Copyright © 2009, Rudolf Pecinovský ICZ 215 Vaše jistota na trhu IT Obsah Pozorovatel (Observer), Posluchač (Listener) ► Zavádí vztah mezi objekty (pozorovateli) reagujícími na změnu stavu (pozorovaného) objektu nebo na jím sledované události. Pozorovatelé se u pozorovaného objektu přihlásí a ten je pak na každou změnu svého stavu či výskyt události upozorní. 063 Motivace Objekt čekající na výskyt nějaké události – má dvě možnosti: ► Neustále se ptát iniciátora události, zda už nastala ● Oslík v 2. dílu Shreka se neustále ptá „Už tam budem?“ ● Řidič čekající na zelenou neustále sleduje semafor ► Dohodne se s iniciátorem, že dohodnutým způsobem oznámí, až událost nastane ● V lůžkovém vlaku vás průvodčí vzbudí před cílovou stanicí ● Řidič na semaforu v klidu čte mapu v očekávání, že ti za ním na něj zatroubí ● SMS oznámí příchod peněz na konto ► Návrhový vzor Pozorovatel řeší problém druhým způsobem, je však třeba dohodnout způsob oznámení výskytu události Copyright © 2009, Rudolf Pecinovský ICZ 217 Charakteristika ► Bývá někdy označován jako vzor Posluchač (Listener) nebo jako vzor vydavatel—předplatitel (publisher—subscriber) ► Ukazuje, jak zabezpečit, aby se objekt včas dozvěděl o události, na kterou čeká, aniž by musel obtěžovat jejího iniciátora neustálými dotazy ► Pozorovatel se přihlásí u pozorovaného objektu, a ten mu při vzniku očekávané události pošle o této události zprávu Copyright © 2009, Rudolf Pecinovský ICZ 218 Posluchač – implementace ► Posluchač (pozorovatel, předplatitel) se musí přihlásit u vysílače (pozorovaného, vydavatele) ► Vysílač je ochoten přijmout přihlášku pouze od objektů implementujících rozhraní definující jak objektu oznámit, že došlo k očekávané události ► Posluchač se po přihlášení o událost dál nestará a hledí si svého ► Až vysílač zavolá dohodnutou metodu, pak zareaguje ► Událostmi řízené programování Copyright © 2009, Rudolf Pecinovský ICZ 219 Implementace ve standardní knihovně ► Ve standardní knihovně je pro aplikaci tohoto vzoru připraveno rozhraní java.util.Observer a třída java.util.Observable ► Použití implementace ze standardní knihovny vyžaduje definici potomka třídy Observable ► Rozhraní Observer deklaruje metodu update(Observable, Object) ● První parametr předává odkaz na pozorovaný objekt, který metodu zavolal. Je definován proto, aby mohl být jeden pozorovatel přihlášen u několika pozorovaných objektů ● Druhý parametr slouží jako schránka na případné parametry Copyright © 2009, Rudolf Pecinovský ICZ 220 Strategie implementace ► Můžeme použít tažnou nebo tlačnou strategii: ► Při tažné strategii si pozorovatel říká pozorovanému objektu o potřebné parametry, ► Při tlačné strategii předá pozorovaný objekt pozorovateli všechny parametry při volání upozorňovací metody Copyright © 2009, Rudolf Pecinovský ICZ 221 Příklad: IListener + IBroadcast public interface IListener<L extends IListener> { public void update( IBroadcast<L> bc, Object ... args ); } public interface IBroadcast<L extends IListener> { public void addListener( L lis ); public void removeListener( L lis ); public void removeAllListeners(); } Copyright © 2009, Rudolf Pecinovský ICZ 222 Vaše jistota na trhu IT Obsah Prostředník (Mediator) 064 ► Odstraní vzájemné vazby mezi řadou navzájem komunikujících objektů tím, že zavede objekt, který bude prostředníkem mezi komunikujícími objekty. Tím zruší přímou vzájemnou závislost komunikujících objektů a umožní upravovat chování každého z nich nezávisle na ostatních. Motivace ► Objekty v jednoduché grafické aplikaci se při pohybu nejprve smažou v původní pozici, aby se pak nakreslily v nové; při tom občas odmažou i část svých kolegů ► Aby objekt mohl zrekonstruovat svůj obraz, musel by mu někdo poslat zprávu, že byl (byť částečně) smazán ► Vysílač je vždy závislý na příjemci, protože změna jeho rozhraní může ovlivnit požadovaný způsob zasílání zpráv ► Když bude každý umět každému poslat zprávu, velmi se zvýší počet vzájemných závislostí, které zhoršují spravovatelnost ● Každá změna nás nutí zkontrolovat všechny závislé objekty ● Musí-li se změnit závislý objekt, dominovým efektem se problém propaguje na všechny objekty, které jsou na něm závislé Copyright © 2009, Rudolf Pecinovský ICZ 224 Doporučené řešení ► Obdobný problém byl i u telefonů – obdobné je i řešení ● Telefony také nejsou spojeny každý s každým, ale spojují se prostřednictvím ústředny ► Definujeme objekt prostředníka, který zprostředkovává veškerou komunikaci objektů ● Vzájemné závislosti objektů se tak omezí na závislost na prostředníku ► V našem projektu nahradíme plátno správcem plátna ● ● ● Má na starosti správný vzhled Když objekt mění svoji podobu, řekne správci Správce pak požádá o překreslení všechny, jichž se to týká ► Prostředník je většinou implementován aplikací návrhového vzoru Pozorovatel Copyright © 2009, Rudolf Pecinovský ICZ 225 Charakteristika ► Doporučuje zjednodušit vzájemnou komunikaci mezi mnoha objekty zavedením prostředníka, na kterého se budou všichni obracet a který jejich zprávy předá požadovanému adresátu ► Pro přeposílání obdržených zpráv bývá často použit návrhový vzor Pozorovatel ► Návrhový vzor Prostředník je výhodné aplikovat: Motivace ● je-li vzájemná komunikace objektů složitá, nebo ● je-li třeba použít komunikující objekty také samostatně ► Oddělením komunikujících objektů docílíme toho, že je můžeme měnit nezávisle na sobě Copyright © 2009, Rudolf Pecinovský ICZ 226 Implementace ► Modifikaci režimu komunikace lze zabezpečit např. definováním dceřiné třídy prostředníka ► Posílaná zpráva je definována jako metoda prostředníka jejímiž parametry jsou součásti zprávy ► Součástí zprávy posílané prostředníku může být i odkaz na příjemce či odesilatele ► Prostředník někdy slouží jako distributor zpráv předávaných všem kolegům (viz SprávcePlátna) – pak většinou implementuje i vzor Pozorovatel Copyright © 2009, Rudolf Pecinovský ICZ 227 Výhody × nevýhody ► Zjednodušuje protokol – nahrazuje vazby m:n vazbami 1:m, které jsou jednodušší na pochopení ► Ruší vazby mezi kolegy a usnadňuje tak jejich znovupoužití ► Odděluje kooperaci mezi objekty od jejich samostatného chování ► Soustřeďuje distribuované chování na jedno místo – to bývá často současně nevýhodou, protože vzniká složitý (příp. supersložitý) objekt – prostředník Copyright © 2009, Rudolf Pecinovský ICZ 228 Použití ► GUI, je-li chování prvků dialogového okna závislé na nastavení některých jiných prvků; při větším množství křížových vazeb je výhodné použít prostředníka ► Distribuce požadavků konfiguračních souborů nebo jinak nastavených parametrů ► Příklad: Plátno × SprávcePlátna Copyright © 2009, Rudolf Pecinovský ICZ 229 Vaše jistota na trhu IT Obsah 07 Dodatečné úpravy 07 ► Most (Bridge) 071 ► Strategie (Strategy) 072 ► MVC (Model – View – Controller) 073 ► Návštěvník (Visitor) 074 ► Pamětník (Memento) 075 ► Interpret (Interpreter) 076 Vaše jistota na trhu IT Obsah Most (Bridge) 071 ► Zavedením rozhraní odděluje abstrakci zprostředkovávající nějakou službu od implementace této služby, takže lze obě dvě měnit nezávisle na sobě. Charakteristika ► Řeší situaci, kdy klient komunikuje se třídou, která je pouze zprostředkovatelem (abstrakcí) nabízené služby. Vlastní realizaci služby má na starosti jiná třída (implementace) ► Návrhový vzor Most doporučuje vložit mezi abstrakci a implementaci rozhraní. Tím je od sebe oddělí a umožní je nezávisle na sobě měnit ► Aplikace vzoru Most umožní definovat implementaci např. v konfiguračním souboru ► Návrhový vzor Most se uplatňuje např. při návrhu ovladačů Copyright © 2009, Rudolf Pecinovský ICZ 232 Implementace ► Při implementaci tohoto vzoru je třeba: ● definovat oddělující rozhraní, ● definovat implementaci, ● definovat, jak abstrakce získá odkaz na implementaci, ● definovat klienta, ● vše rozběhnout Copyright © 2009, Rudolf Pecinovský ICZ 233 Klasicky navržená kalkulačka ► Klasický přístup ● Odděluje se návrh CPU and GUI; – Třídu GUI navrhuje vyučující – Třídu CPU navrhují studenti ● Stisk tlačítka z každé skupiny volá partnerskou metodu v CPU ► Třídy jsou těsně provázané což má své nepříjemné důsledky: ● S každou změnou definice CPU je třeba změnit definic GUI a naopak ● Každá verze studentského zadání vyžaduje vlastní verzi definice GUI ● Chceme-li po studentech, aby u zkoušky svoji třídu upravili, musíme příslušně upravit i námi definované GUI ► Uvedený přístup je nutný v případě neznají-li studenti rozhraní Copyright © 2009, Rudolf Pecinovský ICZ 234 Po aplikaci vzoru Most ► Třída CPU implementuje rozhraní ICPU deklarující tři metody: ● RozměrKláves getRozměr() ● List<String> getPopisky() ● String stisknuto(String label) ► Konstruktor třídy GUI ● Dostane instanci of CPU jako parametr ● Zeptá se na požadovaný rozměr klávesnice a seznam požadovaných popisků tlačítek ● Připraví GUI podle obdržených požadavků ► Běh programu: ● Instance GUI zjistí, jaké tlačítko bylo stisknuto a zašle instanci CPU zprávu s jeho popiskem ● Instance CPU zjistí, co stisk znamená, a vrátí GUI nový obsah displeje Copyright © 2009, Rudolf Pecinovský ICZ 235 Co jsme tím získali ► GUI může spolupracovat s různými CPU; stačí, aby spolupracující CPU implementovala rozhraní ICPU ► Přiblížíme studentům význam a použití rozhraní ► Ukážeme, že tvůrce GUI nemusí znát spolupracující CPU předem, instance GUI se ji může dozvědět až při svém zrodu ► Můžeme do CPU svobodně přidávat další a další funkce, aniž bychom museli jakkoliv upravovat třídu GUI ► Můžeme připravit automatické testy Copyright © 2009, Rudolf Pecinovský ICZ 236 Testování sady programů ► Testovací třída se bude tvářit, že je zvláštní GUI ► Třída Verze dodá požadovanou sadu popisků spolu s testy definujícími požadované reakce na stisk kláves ► Rozhraní ICPU rozšíříme o metodu vracející pořadí řešeného zadání int getZadání() ► Můžeme přidat značkovací rozhraní IGUI, jež budou implementovat třídy, které se chtěji vydávat za GUI Copyright © 2009, Rudolf Pecinovský ICZ 237 Vaše jistota na trhu IT Obsah Strategie (Strategy) 072 ► Definuje množinu vyměnitelných objektů (algoritmů) řešících zadanou úlohu (tj. objektů se stejným rozhraním) a umožňuje mezi nimi dynamicky přepínat. Charakteristika ► Ukazuje, jak je možno zabezpečit přepínání použitých algoritmů za chodu programu ► Oproti vzoru Most se nezabývá výměnou části programu, ale přepínáním mezi několika částmi, které jsou (většinou trvalou) součástí programu ► Vzor se nezaměřuje na architekturu systému, ale na jeho chování Copyright © 2009, Rudolf Pecinovský ICZ 239 Strategie × Stav ► Oproti vzoru Stav není přepínání interní záležitostí modulu, ale pracuje na objednávku klienta ► Společným „rodičem“ jednotlivých stavů nemusí být jen rozhraní, ale může jím být i třída implementující implicitní verze metod ► Tento společný rodič může aplikovat i vzor Šablonová metoda Copyright © 2009, Rudolf Pecinovský ICZ 240 Kalkulačka schopná přepínat režimy Copyright © 2009, Rudolf Pecinovský ICZ 241 Super CPU – DP Strategy ► Použitím vzoru Most uvolníme cestu dalším rozšířením ● Můžeme definovat CPU, které nebudou pracovat pouze s běžnými čísly, ale budou schopny pracovat s čísly v různých soustavách, se zlomky, komplexními čísly, vektory, symboly, … ● Umožním definici SuperCPU, která bude umět přepínat mezi různými CPU ► Přidaná SuperCPU demonstratuje užití návrhového vzoru Strategie Copyright © 2009, Rudolf Pecinovský ICZ 242 Super CPU – DP Strategy Copyright © 2009, Rudolf Pecinovský ICZ 243 Vaše jistota na trhu IT Obsah MVC (Model – View – Controller) 073 ► Odděluje část programu mající na starosti předzpracování příkazů uživatele (tj. zjištění, co od programu vlastně chce), od částí zabezpečujících logiku programu, která uživatelovy příkazy zpracovává, a části, která má na starosti zobrazení výsledků. Charakteristika ► Vzor Model – Pohled – Ovládání (MPO); anglicky Model – View – Controller (MVC) je většinou považován za skupinu vzorů ► Doporučuje rozdělit do samostatných modulů části programu zabývající se ● předzpracováním požadavků uživatele, ● vlastní logikou programu a ● prezentací získaných výsledků ► Do části zabývající se prezentací výsledků patří nejenom vlastní GUI, ale často i kód, který data pro tuto prezentaci připravuje Copyright © 2009, Rudolf Pecinovský ICZ 245 Přínosy použití vzoru ► Snadná implementace zadávání požadavků různými způsoby (klávesnice, myš, pero, hlas, …) ► Možnosti různých podob prezentace výsledků (tabulka, graf, mluvené slovo, …) ► Poznámka: Změny požadavků na způsob zadávání požadavků a prezentaci výsledů patří k nejčastějším ► Usnadnění případných budoucích změn ► Přenositelnost mezi platformami Copyright © 2009, Rudolf Pecinovský ICZ 246 Implementace ► Vzor je spíše obecným doporučním, které jednotlivé vazby nijak konkrétně nespecifikuje ► V jednodušších aplikacích se některé části často slučují Copyright © 2009, Rudolf Pecinovský ICZ 247 Diagram tříd hry Reversi Copyright © 2009, Rudolf Pecinovský ICZ 248 Vaše jistota na trhu IT Obsah Návštěvník (Visitor) 074 ► Umožňuje definovat pro skupinu tříd nové operace jejich instancí, aniž by bylo nutno jakkoliv měnit kód těchto tříd. Charakteristika ► Umožňuje definovat nové operace instancí, aniž by bylo nutno měnit kód jejich tříd ► Hodí se v situacích, kdy se počet tříd již nemění, ale mění se počet metod, které musí jejich instance umět ► Pro každou přidávanou metodu je třeba definovat zvláštní třídu návštěvníka Copyright © 2009, Rudolf Pecinovský ICZ 250 Makroimplementace – Navštívený ► Instance třídy, která má spolupracovat s návštěvníkem, musí definovat metodu pro příjem návštěvníka ► V těle metody je jediný příkaz: předání řízení metodě návštěvníka public class Navštívený { // ... public přijmi( INávštěvník návštěvník, Object param ) { návštěvník.aplikujNa( this, param ); } // ... } Copyright © 2009, Rudolf Pecinovský ICZ 251 Makroimplementace – návštěvník ► Návštěvník musí implementovat rozhraní, které deklaruje přetíženou verzi metody pro každou navštěvovanou třídu ► Aby překladač zvolil správnou přetíženou verzi, nesmí se navštěvované třídy spolehnout na dědění, ale každá musí definovat svoji vlastní, byť stále stejnou verzi přijímací metody ► Pro rozhraní návštěvníka je možné definovat adaptér, který může usnadnit definici návštěvníkových metod ► Adaptér může využít dědičnost a umožnit tak nedefinovat reakce pro některé typy Copyright © 2009, Rudolf Pecinovský ICZ 252 Diagram tříd Copyright © 2009, Rudolf Pecinovský ICZ 253 Příklad rozhraní s adaptérem public interface INávštěvník { public Object aplikujNa( public Object aplikujNa( public Object aplikujNa( public Object aplikujNa( public Object aplikujNa( public Object aplikujNa( public Object aplikujNa( public Object aplikujNa( public Object aplikujNa( public Object aplikujNa( AHýbací APosuvný Čára Čtverec Elipsa Kruh Obdélník Obrázek Text Trojúhelník in, in, in, in, in, in, in, in, in, in, Object Object Object Object Object Object Object Object Object Object param param param param param param param param param param ); ); ); ); ); ); ); ); ); ); //== VNOŘENÉ TŘÍDY ========================================== public static class Adaptér implements INávštěvník { public Object aplikujNa( APosuvný in, Object param) { throw new UnsupportedOperationException(); } public Object aplikujNa( Čára in, Object param ) { return aplikujNa((APosuvný)in, param ); } ICZ Copyright © 2009, Rudolf Pecinovský 254 Mikroimplementace – popis ► Při aplikaci návštěvníkovy metody zavoláme přijímací metodu navštíveného, tj. objektu, za nějž bude návštěvník provádět akci ► Navštívený v této metodě zavolá svoji verzi návštěvníkovy metody, která vše provede místo navštíveného, a vrátí výsledek, který navštívený předá, jako by připravil sám Copyright © 2009, Rudolf Pecinovský ICZ 255 Mikroimplementace – sekvenční diagram Pořadí požadovaných akcí: 1. Akce A na objektu 1 2. Akce A na objektu 2 3. Akce B na objektu 1 Před vyvoláním akce je třeba získat návštěvníka, který akci provede Copyright © 2009, Rudolf Pecinovský ICZ 256 Vaše jistota na trhu IT Obsah Pamětník (Memento) ► Zabezpečuje uchovávání stavů objektů, aby je bylo v případě potřeby možno uvést do původního stavu. Přitom zabezpečuje, aby při uchovávání stavu nebylo narušeno ukrytí implementace. 075 Charakteristika ► Ukazuje, jak je možno uchovávat informace o stavech objektů pro potřeby jejich pozdějšího opětného nastavení ► Hlavním problémem je nutnost maximálního skrytí informací o instanci, jejíž stav chceme uložit Copyright © 2009, Rudolf Pecinovský ICZ 258 Implementace ► Instance uchovávající stav mohou implementovat pouhé značkovací rozhraní ► Vlastní pamětníci bývají většinou definováni jako vnitřní třídy ► Je třeba si rozmyslet, kdy raději uchovávat informace o provedených akcích a kdy výsledné pozice ► Místo celých pozic je možno uchovávat jen jejich inkrementální změny Copyright © 2009, Rudolf Pecinovský ICZ 259 Vaše jistota na trhu IT Obsah Interpret (Interpreter) ► Definuje reprezentaci gramatických pravidel jazyka pomocí systému tříd, a současně definuje interpret tohoto jazyka pomocí metod instancí těchto tříd. 076 Charakteristika ► Ukazuje, jak je možno relativně jednoduše implementovat interpret vlastního jazyka ► Zabývá se pouze vnitřní reprezentací programu a její interpretací ► Neřeší převod textového zápisu do této reprezentace, tj. nezahrnuje lexikální a syntaktickou analýzu ► Klasický překlad: ● Lexikální analýza: najde symboly jazyka ● Syntaktická analýza: Vytvoří vnitřní reprezentaci (VR) ● Sémantická analýza: Vytvoří kód nebo VR přímo interpretuje Copyright © 2009, Rudolf Pecinovský ICZ 261 Implementace ► Převádí gramatická pravidla do systému tříd ► Instance těchto tříd pak představují části vytvářeného programu ► Je velmi efektivní při implementaci jednodušších gramatik ► U složitějších gramatik je výhodnější sáhnout po klasických postupech Copyright © 2009, Rudolf Pecinovský ICZ 262 Výhody ► Umožňuje snadnou realizaci i poměrně rafinovaných operací, mezi něž patří např. substituce proměnné výrazem apod. ► Přeložené programy je možné spouštět nad různými sadami hodnot jejich proměnných uložených v instanci nějakého kontextu Copyright © 2009, Rudolf Pecinovský ICZ 263 Příklad 1: Regulární výraz – Gramatika ► Výraz ::= Literál | Volba | Posloupnost | Opakování | '(' Výraz ')' ► Volba ::= Výraz '|' Výraz ► Posloupnost ::= Výraz '&' Výraz ► Opakování ::= Výraz '*' ► Literál ::= 'a' | 'b' | 'c' | ... { 'a' | 'b' | 'c' | ... }* Copyright © 2009, Rudolf Pecinovský ICZ 264 Příklad 1: Diagram tříd popsané gramatiky ► Výraz ::= Literál | Volba | Posloupnost | Opakování | '(' Výraz ')' Výraz +interpretuj() Volba Posloupnost -volba1 : Výraz -volba2 : Výraz -posloupnost : List<Výraz> Copyright © 2009, Rudolf Pecinovský Opakování ICZ -výraz : Výraz Literál -literál : String 265 Příklad 1 – diagram objektů ► Diagram objektů pro výraz: padají & (žáby | trakaře) * posloupnost první druhý literál_1 opakování "padají" výraz volba volba1 volba2 literál_2 "žáby" Copyright © 2009, Rudolf Pecinovský ICZ literál_3 "trakaře" 266 Příklad 1 – diagram objektů ► Diagram objektů pro výraz: padají & (žáby | trakaře) * ● new Posloupnost( "padají", new Opakování( new Volba( "žáby", "trakaře") posloupnost první druhý literál_1 opakování "padají" výraz volba volba1 volba2 literál_2 literál_3 "žáby" Copyright © 2009, Rudolf Pecinovský "trakaře" ICZ 267 Příklad 2: Aritmetický výraz – gramatika ► Konstanta ::= '#'číslo'#' ► Proměnná ::= Identifikátor ► Poločlen ::= Proměnná | Konstanta | '(' Výraz ')' ► Člen ::= Poločlen | '+'Poločlen | '-'Poločlen ► Součin ::= Člen | Součin '*' Člen | Součin '/' Člen ► Součet ::= Součin | Součet '+' Součin | Součet '–' Součin ► Výraz ::= Součet Copyright © 2009, Rudolf Pecinovský ICZ 268 Diagram tříd interpretu aritmetických výrazů Příklad 2: Diagram tříd interpretu aritmetických výrazů Copyright © 2009, Rudolf Pecinovský ICZ 269 Příklad 3: Jazyk robota – Gramatika ► Program ::= [ DefiniceProcedury | DefiniceFunkce ]... Příkaz ► DefiniceProcedury ::= $Identifikátor Příkaz ► DefiniceFunkce ::= §Identifikator. PříkazVracejícíHodnotu ► Identifikátor ::= ZobecněnéPismeno [ ZobecněnéPismeno | Číslice ]... ► ZobecněnéPísmeno ::= Písmeno | _ l Komentář ::= « Text » ► Podmínka ::= ?Funkce Příkaz [ : Příkaz ] l Volání ::= !Procedura ► Opakování ::= @Číslo Příkaz... l While ::= ¤Funkce Příkaz... ► Příkaz ::= Podmínka | Opakování | While | Volání | Break | Return | { [Příkaz ]... } ► Procedura ::= IdentifikátorDefinovanéProcedury | IdentifikátorAtomickéProcedury ► IdentifikátorAtomickéProcedury ::= krok | vlevoVbok | poloz | zvedni ► Funkce ::= IdentifikátorDefinovanéFunkce | IdentifikátorAtomickéFunkce ► IdentifikátorAtomickéFunkce ::= jeZnacka | jeZed | jeRobot | jeVychod ► NastavHodnotu ::= +++ | --- | ^Funkce Copyright © 2009, Rudolf Pecinovský ICZ (l Break ::= >>> l Return ::= ###) 270 Vaše jistota na trhu IT Děkuji za pozornost Rudolf Pecinovský [email protected] [email protected] ICQ: 158 156 600