Návrhové vzory - Objektově orientované programování a jeho výuka

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

similar documents