Rudolf PECINOVSKÝ rudolf@pecinovsky.cz Návrhové vzory Rudolf PECINOVSKÝ rudolf@pecinovsky.cz.

Slides:



Advertisements
Podobné prezentace
(instance konkrétní třídy)
Advertisements

Vlastní skript může být umístěn: v hlavičce stránky v těle stránky
Dynamické dokumenty na straně klienta Informatika pro ekonomy II.
Tabulky v MS ACCESS Autorem materiálu a všech jeho částí, není-li uvedeno jinak, je Mgr. Jiří Novák.
Přednáška č. 3 Normalizace dat, Datová a funkční analýza
HYPERTEXT PREPROCESSOR. PROGRAMOVÁNÍ. DEFINICE POJMŮ Problém Problém nevyřešený, nežádoucí stav obvykle vyžaduje nějaké řešení Neřešitelný problém Neřešitelný.
Vaše jistota na trhu IT Quo vadis, programování? Rudolf PECINOVSKÝ 2012 – e-bezpečnost v Kraji Vysočina 1.
Databázové systémy Přednáška č. 2 Proces návrhu databáze.
ÚVOD DO CPP 7 Dědičnost - pokračování
PJV151 Vnořené a vnitřní členy mohou být členy tříd a interfejsů. Je-li X obalem Y a Y je obalem Z, pak Z získá jméno X$Y$Z - kompilací vzniknou classy.
Počítače a programování 1. Obsah přednášky Výjimky - základní typy výjimek Způsoby zpracování výjimek.
C# pro začátečníky Mgr. Jaromír Osčádal
ADT Strom.
NÁSOBENÍ ČÍSLEM 10 ZÁVĚREČNÉ SHRNUTÍ
Dělitelnost přirozených čísel
VY_32_INOVACE_INF_RO_12 Digitální učební materiál
Chain of responsibility Martin Malý prezentace na předmět Návrhové vzory (PRG024) na MFF UK
Zábavná matematika.
MS PowerPoint Prezentační manažer Kapitola 13.1 (Schránka a operace se schránkou)
State. State – kontext a problém Kontext  chování objektu má záviset na jeho stavu, který se typicky mění za běhu Neflexibilní řešení  metody obsahují.
Jazyk vývojových diagramů
Čtení myšlenek Je to až neuvěřitelné, ale skutečně je to tak. Dokážu číst myšlenky.Pokud mne chceš vyzkoušet – prosím.
Vyučovací hodina 1 vyučovací hodina: Opakování z minulé hodiny 5 min Nová látka 20 min Procvičení nové látky 15 min Shrnutí 5 min 2 vyučovací hodiny: Opakování.
Dělení se zbytkem 8 MODERNÍ A KONKURENCESCHOPNÁ ŠKOLA
Náhoda, generátory náhodných čísel
Databáze Tabulky a typy dat VY_32_INOVACE_7B11. Proč datové typy ? Abychom mohli do tabulky vložit data, musíme jednotlivým polím (sloupcům) přiřadit.
Hromadná korespondence, makro
Objektové programování
Jedenácté cvičení Vlákna. Java cv112 Vlákna Operační systém Mutitasking – více úloh se v operačním programu vykonává „současně“ Java Multithreading -
Hromadná korespondence
TEXTOVÝ EDITOR.
Strategy. Strategy – „All-in-1“ na začátek class AStrategy { public: virtual void Algorithm()=0; protected: AStrategy(); }; class SpecificStrategy: public.
Composite [kompozit, ne kompozajt]. Composite Výslovnost  kompozit, ne kompozajt Účel  Popisuje, jak postavit hierarchii tříd složenou ze dvou druhů.
6. cvičení Polymorfismus
Počítače a programování 1
JavaScript Podmínky, cykly a pole.
Vaše jistota na trhu IT Interní datové typy Rudolf Pecinovský
KIV/PPA1 cvičení 8 Cvičící: Pavel Bžoch. Osnova cvičení Objekty v Javě Třída Konstruktor Metody Metody a proměnné třídy x instance Program sestávající.
OSNOVA: a) Úvod do OOPb) Třídy bez metod c) Třídy s metodamid) Konstruktory a destruktory e) Metody constf) Knihovní třídy g) Třídy ve tříděh) Přetížení.
Algoritmizace a programování Objektově orientované programování - 16 Mgr. Josef Nožička IKT Algoritmizace a programování
2 Ing. Jan Keprt Centrální správa uživatelů 3 Jak to bylo dosud Bylo třeba nastavení uživatelů provést zvlášť, v každém modulu samostatně. Uživatel si.
KONTROLNÍ PRÁCE.
C# - předávání parametrů Centrum pro virtuální a moderní metody a formy vzdělávání na Obchodní akademii T.G. Masaryka, Kostelec nad Orlicí.
IB111 Programování a algoritmizace
Práce se šablonami v MS Word 2007
Porovnání výroby a prodejů vozidel ve světě
Dokumentace informačního systému
Dědičnost - inheritance dědičnost je jednou z forem znovupoužitelnosti dědičnost je jednou z forem znovupoužitelnosti B A Třída A je předkem třídy B Třída.
7. Typ soubor Souborem dat běžně rozumíme uspořádanou množinu dat, uloženou mimo operační paměť počítače (na disku). Pascalský soubor je abstrakcí skutečného.
Metodika objektového přístupu při tvorbě překladačů. Marek Běhálek Informatika a aplikovaná matematika FEI VŠB-TU Ostrava.
Šesté cvičení Výjimky Balíky.
Orbis pictus 21. století Tato prezentace byla vytvořena v rámci projektu.
Počítače a programování 1 7.přednáška. Základy Pole ve třídách a metodách Pole Arrays.
Analýza informačního systému. Podrobně zdokumentovaný cílový stav Paramentry spojené s provozem systému – Cena – Přínosy – Náklady a úspory – …
OPERAČNÍ SYSTÉMY.
Jazyk C A0B36PRI - PROGRAMOVÁNÍ Část II.
Vícerozměrná pole (1) Jazyk C povoluje, aby pole mělo více rozměrů (dimenzí) než jeden Z vícerozměrných polí bývá nejčastěji použí-váno pole dvourozměrné.
Vaše jistota na trhu IT Rozhraní a implementace Rudolf PECINOVSKÝ 2012 – Vývoj bezpečných aplikací 1.
Úvod do databází zkrácená verze.
SOFTWAROVÁ PODPORA PRO VYTVÁŘENÍ FUZZY MODELŮ Knihovna fuzzy procedur Ing. Petr Želasko, VŠB-TU Ostrava.
NÁZEV ŠKOLY:SOŠ Net Office, spol. s r.o. Orlová Lutyně
Moduly.
Vícerozměrná pole (1) Jazyk C povoluje, aby pole mělo více rozměrů (dimenzí) než jeden Z vícerozměrných polí bývá nejčastěji použí-váno pole dvourozměrné.
Návrhový vzor Flyweight
Operační systémy 9. Spolupráce mezi procesy
Lazy load Použity informace z knihy Patterns of Enterprise Application Architecture od M. Fowlera Jan Sládek.
Příkazy cyklu (1) Umožňují vícekrát (nebo ani jednou) pro-vést určitý příkaz Jazyk C rozlišuje příkaz cyklu: s podmínkou na začátku: obecný tvar: while.
C# přehled vlastností.
Algoritmizace a datové struktury (14ASD)
Monitor Object 1.
Transkript prezentace:

Rudolf PECINOVSKÝ rudolf@pecinovsky.cz Návrhové vzory Rudolf PECINOVSKÝ rudolf@pecinovsky.cz

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

Podrobný obsah 1/2 00: Předehra 01: První kontakt 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 01: První kontakt Jednoduchá tovární metoda (Simple factory method) 011 Neměnné objekty (Immutable objects) 012 Přepravka (Crate) 013 Služebník (Servant) 014 Prázdný objekt (Null Object) 015 02: Vzory řešící počet instanci Společné vlastnosti 020 Knihovní třída (Library class, Utility) 021 Jedináček (Singleton) 022 Výčtový typ (Enumeration) 023 Fond (Pool) 025 Originál (Original) 024 Muší váha (Flyweight) 026 03: Skrývání implementace Zástupce (Proxy) 031 Příkaz (Command) 032 Iterátor (Iterator) 033 Stav (State) 034 Šablonová metoda (Template Method) 035 Copyright © 2009, Rudolf Pecinovský ICZ

Podrobný obsah 1/2 04: Optimalizace rozhraní Fasáda (Facade) 041 Adaptér (Adapter) 042 Strom (Composite) 043 05: Otevřenost variantním řešením Tovární metoda (Factory method) 051 Prototyp (Prototype) 052 Stavitel (Builder) 053 Abstraktní továrna (Abstract factory) 054 06: Ochrana před rozrůstáním Dekorátor (Decorator) 061 Řetěz odpovědnosti (Chain of responsibility) 062 Pozorovatel (Observer) 063 Prostředník (Mediator) 064 07: Dodatečné úpravy 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 Copyright © 2009, Rudolf Pecinovský ICZ

00 Předehra 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

01 První kontakt 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ů Výhody: 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

Jednoduchá tovární metoda 010 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

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

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

Příklad ze standardní knihovny – Integer private static class IntegerCache { private IntegerCache(){} 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); Pomocná vnořená třída; zásobník instancí Tovární metoda pro získání instance Copyright © 2009, Rudolf Pecinovský ICZ

Neměnné objekty (Immutable objects ) 012 Obsah Neměnné objekty (Immutable objects ) 012 Neměnný objekt je hodnotový objekt, u nějž není možno změnit jeho hodnotu.

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

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 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

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

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

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

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í 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 Objektové Odkazové Hodnotové Proměnné Neměnné Datové typy Primitivní Copyright © 2009, Rudolf Pecinovský ICZ

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, j * z.j ); //... Copyright © 2009, Rudolf Pecinovský ICZ

Přepravka (Crate, Transport Object) 013 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

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

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

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 ); Převádí akci na svoji přetíženou verzi Copyright © 2009, Rudolf Pecinovský ICZ

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

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

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

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 pozice ); void setPozice( int x, int y ); Zato nabízí možnost volat metody: void přesunO ( IPosuvný ip, int dx, int dy ); void přesunNa( IPosuvný ip, int x, int y ); void přesunNa( IPosuvný ip, Pozice pozice ); Copyright © 2009, Rudolf Pecinovský ICZ

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

Prázdný objekt (Null Object) 015 Obsah Prázdný objekt (Null Object) 015 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.

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

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

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

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

02 Vzory řešící počet instancí 02 Obsah Společné vlastnosti 020 Knihovní třída (Library class, Utility) 021 Jedináček (Singleton) 022 Výčtový typ (Enumeration) 023 Fond (Pool) 025 Originál (Original) 024 Muší váha (Flyweight) 026

Společné vlastnosti vzorů řešících problematiku počtu instancí 020 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

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

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

Knihovní třída (Library class, Utility) 021 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

Příklady java.lang.Math java.lang.Runtime java.lang.System java.util.Arrays java.util.Collections Copyright © 2009, Rudolf Pecinovský ICZ

Jedináček (Singleton) 022 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

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

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

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

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

Problémy při více vláknech První vlákno Druhé vlákno Jedináček.getInstance() { if( JEDINÁČEK == null ) JEDINÁČEK = new Jedináček(); } return JEDINÁČEK; Copyright © 2009, Rudolf Pecinovský ICZ

Problémy při více vláknech První vlákno Druhé vlákno Jedináček.getInstance() { if( JEDINÁČEK == null ) JEDINÁČEK = new Jedináček(); } return JEDINÁČEK; Copyright © 2009, Rudolf Pecinovský ICZ

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 ) { JEDINÁČEK = new Jedináček(); } return JEDINÁČEK; Copyright © 2009, Rudolf Pecinovský ICZ

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

Výčtový typ (Enumeration) 023 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

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

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

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); $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) { super(s, i); Deklarace hodnot (veřejných konstant) daného typu Pole hodnot daného typu Statický inicializační blok inicializující konstanty a pole s odkazy na ně Metoda vracející pole hodnot daného typu Metoda vracející hodnotu po zadání jejího názvu Soukromý konstruktor zabraňující vytváření dalších instancí Copyright © 2009, Rudolf Pecinovský ICZ

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

„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

Vytváření instancí třídy Směr8 public enum Směr8 { //== HODNOTY VÝČTOVÉHO TYPU ================================================== VÝCHOD ( 1, 0, "V", "VYCHOD" ), SEVEROVÝCHOD( 1, -1, "SV", "SEVEROVYCHOD" ), 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 =============================================== public static final int SMĚRŮ = 8; private static final int MASKA = 7; private static final Map<String,Směr8> názvy = new HashMap<String,Směr8>( SMĚRŮ*3 ); private static final int[][] posun = new int[SMĚRŮ][2]; private static final Směr8[] SMĚRY = values(); static { for( Směr8 s : SMĚRY ) { posun[s.ordinal()][0] = s.přepravka.dx; posun[s.ordinal()][1] = s.přepravka.dy; názvy.put( s.přepravka.zkratka, s ); názvy.put( s.přepravka.název, s ); názvy.put( s.přepravka.názevBHC,s ); s.přepravka = null; } //== DOČASNÉ ATRIBUTY INSTANCÍ =============================================== private static class Přepravka { int dx, dy; String zkratka, název, názevBHC; Přepravka přepravka; //== KONSTRUKTORY A TOVÁRNÍ METODY =========================================== private Směr8( int dx, int dy, String zkratka, String názevBHC ) { přepravka = new Přepravka(); přepravka.dx = dx; přepravka.dy = dy; přepravka.zkratka = zkratka; přepravka.název = name(); přepravka.názevBHC = názevBHC; Deklarace hodnot (veřejných konstant) daného typu Přepravka pro dočasné uchování hodnot zadaných konstruktoru, než je bude možno uložit do mapy a příslušných polí Mapa pro získání instance zadaného názvu či zkratky Konstruktor ukládá všechny parametry do přepravky, aby je bylo možno vyzvednout ve statickém inicializačním bloku Instance si svá data pamatují v polích, která jsou atributy třídy Statický inicializační blok inicializující pole a mapu hodnotami z přepravky Copyright © 2009, Rudolf Pecinovský ICZ

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

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

Příklad funkčního výčtového typu Metody instancí musí překrývat metodu předka, 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 public enum Operace { PLUS { public int proveď( int a, int b) { return a + b; } }, MINUS { return a - b; }; public abstract proveď( int a, int b ); //... Operace op = PLUS; int výsledek = op.proveď( x, y ); Copyright © 2009, Rudolf Pecinovský ICZ

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

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 static void test() { den( LENOCH ); den( ČILOUŠ ); den( PRACANT ); public static void main( String[] args ) { test(); Copyright © 2009, Rudolf Pecinovský ICZ

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

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

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

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

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

Příklad IFond Fond Brownův pohyb molekul Copyright © 2009, Rudolf Pecinovský ICZ

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(); * volná instance, zařadí žadatele do fronty čekajících. */ public T dejPočkám(); * 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

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

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

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

Implementace Stav objektu je rozdělen do dvou částí Příklady 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

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

03 Skrývání implementace 03 Obsah Zástupce (Proxy) 031 Příkaz (Command) 032 Iterátor (Iterator) 033 Stav (State) 034 Šablonová metoda (Template Method) 035

Obsah Zástupce (Proxy) 031 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.

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

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

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

Diagram tříd zástupců Copyright © 2009, Rudolf Pecinovský ICZ

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

Vzdálený zástupce – schéma Copyright © 2009, Rudolf Pecinovský ICZ

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

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

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

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 void nakresli(Kreslítko g); public String toString(); public IZastávka napoj( int x, int y ); public void napojNa( Zastávka předchozí ); public Zastávka zruš(); } Copyright © 2009, Rudolf Pecinovský ICZ

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

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

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

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

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 Připravíme „proměnnou“ Až budeme vědě, co se má dělat, vložíme do proměnné kód Až budeme potřebovat kód provést, vytáhneme jej z proměnné a provedeme Copyright © 2009, Rudolf Pecinovský ICZ

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

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

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

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

Zdrojový kód metody max(Collection<T>) //Deklarace typových parametrů jsou zjednodušené public static <T extends Comparable<T>> T max(Collection<T> coll) { 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; Kolekcí budeme procházet pomocí iterátoru 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

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(); while(i.hasNext()) { T next = i.next(); if (comp.compare(next, candidate) > 0) candidate = next; } return candidate; private interface SelfComparable extends Comparable<SelfComparable> {} Je-li dodán komparátor, připraví si iterátor a kandidáta na maximum Je-li zadán prázdný odkaz na komparátor, zkusíme verzi bez komparátoru Pak pokračuje stejně jako minule, jenom s jinou metodou porovnání hodnot Verze bez komparátoru vyžaduje nativně porovnatelné objekty – definuje si soukromé vnořené rozhraní, na něž se pokusí instance přetypovat Copyright © 2009, Rudolf Pecinovský ICZ

Příklad: Třídění nativní a modulo public class Modulo2 implements Comparable<Modulo2> { private final int hodnota; 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); private static class Comp implements Comparator<Modulo2> public int compare( Modulo2 a, Modulo2 b) { if( a.modul != b.modul ) return (a.modul - b.modul); else return a.hodnota - b.hodnota; } Objekt má dva atributy: hodnotu a modul Vyrobím pole objektů Setřídím je dvěma způsoby Copyright © 2009, Rudolf Pecinovský ICZ

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] Comparator:[41, 61, 81, 42, 52, 3, 23, 23, 44, 74, 85, 46, 56, 17, 47, 18, 88, 88, 79, 99] public class Modulo2 implements Comparable<Modulo2> { private final int hodnota; 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); private static class Comp implements Comparator<Modulo2> { public int compare( Modulo2 a, Modulo2 b) { if( a.modul != b.modul ) return (a.modul - b.modul); else return a.hodnota - b.hodnota; } Copyright © 2009, Rudolf Pecinovský ICZ

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

Obsah Iterátor (Iterator) 033 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.

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

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

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

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

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

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

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

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

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

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

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í++ ); public void remove() { throw new UnsupporteOperationException(); Protože je definována uvnitř třídy, má přístup k soukromým složkám jejích instancí Neplnohodnotná implementace – volání metody způsobí chybu Copyright © 2009, Rudolf Pecinovský ICZ

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ší(); if( další == null ) break; //----------> System.out.print("Obslouženo: " + další); System.out.println("===== Fronta obsloužena ====="); Copyright © 2009, Rudolf Pecinovský ICZ

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 void aplikuj( IPříkaz příkaz ) {/*tělo*/} public void setFilter( FileFilter filtr ) { this.filtr = filtr; } public void setComparator( Comparator<File> comparator ) {/*tělo*/} public String toString() {/*tělo*/} public 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*/} Dávkový iterátor Návrhový vzor Příkaz Iterátor Copyright © 2009, Rudolf Pecinovský ICZ

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

Obsah Stav (State) 034 Ř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.

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

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

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

Šablonová metoda (Template Method) 035 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

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

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

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 void init() { public void start() { public void stop() { public void destroy() { } Copyright © 2009, Rudolf Pecinovský ICZ

Příklad: MIDlety Start Čekající Aktivní Ukončený konstruktor startApp pauseApp Aktivní destroyApp destroyApp destroyApp Ukončený Copyright © 2009, Rudolf Pecinovský ICZ

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

04 Optimalizace rozhraní 04 Obsah Fasáda (Facade) 041 Adaptér (Adapter) 042 Strom (Composite) 043

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

Komunikace tříd před a po použití fasády Příklad: javax.swing.JOptionPane Copyright © 2009, Rudolf Pecinovský ICZ

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

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

Charakteristika Účel Nasazení 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

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

Metoda s definovatelnou implementací používající vzor Šablonová metoda Příklad public interface IPosuvný extends IKreslený { //== DEKLAROVANÉ METODY ================================= public Pozice getPozice(); public void setPozice( Pozice pozice ); public void setPozice( int x, int y ); //== 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 ) { public void setPozice( Pozice pozice ) { setPozice( pozice.x, pozice.y ); Adaptér je definován jako třída vnořená do rozhraní, na něž bude své potomky adaptovat 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

Možná implementace adaptéru – předka public interface IPosuvný extends IKreslený { //== DEKLAROVANÉ METODY ================================= public Pozice getPozice(); public void setPozice( Pozice pozice ); public void setPozice( int x, int y ); //== VNOŘENÉ TŘÍDY ====================================== public static class Adaptér extends IKreslený.Adaptér implements IPosuvný public Pozice getPozice(){ throw new UnsupportedOperationException(); } public void setPozice( Pozice pozice ){ setPozice( pozice.x, pozice.y ); public void setPozice( int x, int y ) { Adaptér je definován jako vnořená třída rozhraní, na něž bude své potomky adaptovat Metoda s definovatelnou implementací používající vzor Šablonová metoda Metody, o jejichž implementaci se uživatel může rozhodnout podle potřeby Copyright © 2009, Rudolf Pecinovský ICZ

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

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

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

Obsah Strom (Composite) 043 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ů.

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

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

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

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

Tovární metoda (Factory method) 051 Obsah Tovární metoda (Factory method) 051 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.

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

Diagram tříd vzoru Tovární metoda Copyright © 2009, Rudolf Pecinovský ICZ

Aplikace tovární metody v knihovně kontejnerů Copyright © 2009, Rudolf Pecinovský ICZ

Kolekce – továrny na iterátory Copyright © 2009, Rudolf Pecinovský ICZ

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

Příklad GoF/Xiaoping: Bludiště Copyright © 2009, Rudolf Pecinovský ICZ

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

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; maze.addRoom(room1); ; 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) {/*...*/} Implicitní definice mohou být nahrazeny abstraktními Copyright © 2009, Rudolf Pecinovský ICZ

Obsah Prototyp (Prototype) 052 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.

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

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

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

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

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

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

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

Kdy co Obecně bývá výhodnější Nemusí tomu tak ale být vždy 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

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

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

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

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

Příklad GoF/Xiaoping: Bludiště GoTo FactoryMethod Copyright © 2009, Rudolf Pecinovský ICZ

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

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

Ří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

Diagram tříd návrhového vzoru Stavitel Copyright © 2009, Rudolf Pecinovský ICZ

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

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

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

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

Příklad GoF/Xiaoping: Bludiště GoTo Prototype Výkonný objekt GoTo FactoryMethod Řídící objekt Copyright © 2009, Rudolf Pecinovský ICZ

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

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

Abstraktní továrna (Abstract factory) 054 Obsah Abstraktní továrna (Abstract factory) 054 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ů.

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

Schéma Principiální schéma vzoru Abstraktní tovární metoda Copyright © 2009, Rudolf Pecinovský ICZ

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

Diagram tříd objektů virtuálního světa Copyright © 2009, Rudolf Pecinovský ICZ

Diagram tříd simulace virtuálního světa Copyright © 2009, Rudolf Pecinovský ICZ

Příklad GoF/Xiaoping: Bludiště GoTo FactoryMethod GoTo Prototype GoTo Builder Copyright © 2009, Rudolf Pecinovský ICZ

Příklad GoF/Xiaoping: Bludiště src public class MazeGameAbstractFactory { public static Maze createMaze(MazeFactory factory) { Maze maze = factory.makeMaze(); Room room1 = factory.makeRoom(1); Room room2 = factory.makeRoom(2); //... Door door1 = factory.makeDoor(room1, room2); Door door2 = 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

06 Ochrana před rozrůstáním 06 Obsah Dekorátor (Decorator) 061 Řetěz odpovědnosti (Chain of responsibility) 062 Pozorovatel (Observer) 063 Prostředník (Mediator) 064

Dekorátor (Decorator) 061 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

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

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

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

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

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

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

Koncepce čtení a zápisu dat v java.io Copyright © 2009, Rudolf Pecinovský ICZ

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

+ / – Výhody Nevý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

Řetěz odpovědnosti 062 (Chain of responsibility) Obsah Řetěz odpovědnosti 062 (Chain of responsibility) 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

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

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

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

Pozorovatel (Observer), Posluchač (Listener) 063 Obsah Pozorovatel (Observer), Posluchač (Listener) 063 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í.

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

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

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

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

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

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

Prostředník (Mediator) 064 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

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

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: 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ě Motivace Copyright © 2009, Rudolf Pecinovský ICZ

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

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

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

07 Dodatečné úpravy 07 Most (Bridge) 071 Strategie (Strategy) 072 Obsah 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

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

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

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

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

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

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

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

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

Kalkulačka schopná přepínat režimy Copyright © 2009, Rudolf Pecinovský ICZ

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

Super CPU – DP Strategy Copyright © 2009, Rudolf Pecinovský ICZ

MVC (Model – View – Controller) 073 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

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

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

Diagram tříd hry Reversi Copyright © 2009, Rudolf Pecinovský ICZ

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

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

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

Diagram tříd Copyright © 2009, Rudolf Pecinovský ICZ

Příklad rozhraní s adaptérem public interface INávštěvník { public Object aplikujNa( AHýbací in, Object param ); public Object aplikujNa( APosuvný in, Object param ); public Object aplikujNa( Čára in, Object param ); public Object aplikujNa( Čtverec in, Object param ); public Object aplikujNa( Elipsa in, Object param ); public Object aplikujNa( Kruh in, Object param ); public Object aplikujNa( Obdélník in, Object param ); public Object aplikujNa( Obrázek in, Object param ); public Object aplikujNa( Text in, Object param ); public Object aplikujNa( Trojúhelník in, Object 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 ); public Object aplikujNa( Text in, Object param ) { public Object aplikujNa( AHýbací in, Object param ) { public Object aplikujNa( Obrázek in, Object param ) { return aplikujNa((AHýbací)in, param ); public Object aplikujNa( Trojúhelník in, Object param){ public Object aplikujNa( Elipsa in, Object param ) { public Object aplikujNa( Kruh in, Object param ) { return aplikujNa((Elipsa)in, param ); public Object aplikujNa( Obdélník in, Object param ) { public Object aplikujNa( Čtverec in, Object param ) { return aplikujNa((Obdélník)in, param ); Copyright © 2009, Rudolf Pecinovský ICZ

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

Mikroimplementace – sekvenční diagram Pořadí požadovaných akcí: Akce A na objektu 1 Akce A na objektu 2 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

Obsah Pamětník (Memento) 075 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.

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

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

Interpret (Interpreter) 076 Obsah Interpret (Interpreter) 076 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.

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

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

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

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

Příklad 1: Diagram tříd popsané gramatiky Výraz ::= Literál | Volba | Posloupnost | Opakování | '(' Výraz ')' Copyright © 2009, Rudolf Pecinovský ICZ

Příklad 1 – diagram objektů Diagram objektů pro výraz: padají & (žáby | trakaře) * Copyright © 2009, Rudolf Pecinovský ICZ

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") Copyright © 2009, Rudolf Pecinovský ICZ

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

Příklad 2: Diagram tříd interpretu aritmetických výrazů Copyright © 2009, Rudolf Pecinovský ICZ

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 (l Break ::= >>> l Return ::= ###) Copyright © 2009, Rudolf Pecinovský ICZ

Děkuji za pozornost Rudolf Pecinovský rudolf@pecinovsky.cz pecinovsky@java.cz ICQ: 158 156 600

Copyright © 2009, Rudolf Pecinovský ICZ

Pgm Používaná písma a objekty Pgm Příliš žluťoučký kůň úpěl ďábelské ódy (Demi) Pgm Příliš žluťoučký kůň úpěl ďábelské ódy (Medium) Pgm Příliš žluťoučký kůň úpěl ďábelské ódy (Cond) Příliš žluťoučký kůň úpěl ďábelské ódy (Heavy) Příliš žluťoučký kůň úpěl ďábelské ódy (Franklin Gothic Book) Příliš žluťoučký kůň úpěl ďábelské ódy (Comic Sans MS) Příliš žluťoučký kůň úpěl ďábelské ódy (Consolas) Příliš žluťoučký kůň úpěl ďábelské ódy Příliš žluťoučký kůň úpěl ďábelské ódy Opakování Program Keyword Příliš žluťoučký kůň úpěl ďábelské ódy Copyright © 2009, Rudolf Pecinovský ICZ