Využijte sílu jazyka C# při řešení programátorských problémů
C#
návrhové vzory
Judith Bishopová
C# návrhové vzory
Judith Bishopová
C# 3.0 Design Patterns Judith Bishop © ZONER software, a.s., 2010. Authorized translation of the English edition of C# 3.0 Design Patterns © 2007 O'Reilly Media Inc. This translation is published and sold by permission of O'Reilly Media Inc., the owner of all rights to publish and sell the same. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from O'Reilly Media Inc. © ZONER software, a.s., 2010. Autorizovaný překlad originálního anglického vydání knihy C# 3.0 Design Patterns © 2007 O'Reilly Media Inc. Překlad je vydán a prodáván s výslovným svolením O'Reilly Media Inc., vlastníkem veškerých práv na vydání i prodej tohoto titulu. Žádná část této publikace nesmí být reprodukována nebo předávána žádnou formou nebo způsobem, elektronicky ani mechanicky, včetně fotokopií, natáčení ani žádnými jinými systémy pro ukládání bez výslovného svolení O'Reilly Media Inc.
C# – návrhové vzory Autor: Judith Bishopová Copyright © ZONER software, a.s. Vydání první v roce 2010. Všechna práva vyhrazena. Zoner Press Katalogové číslo: ZR805 ZONER software, a.s. Nové sady 18, 602 00 Brno Překlad: Jiří Koutný Odborná korektura: Mgr. Martin Kruliš a Miroslav Kučera Šéfredaktor: Ing. Pavel Kristián DTP: Miroslav Kučera
Informace, které jsou v této knize zveřejněny, mohou byt chráněny jako patent. Jména produktů byla uvedena bez záruky jejich volného použití. Při tvorbě textů a vyobrazení bylo sice postupováno s maximální péčí, ale přesto nelze zcela vyloučit možnost výskytu chyb. Vydavatelé a autoři nepřebírají právní odpovědnost ani žádnou jinou záruku za použití chybných údajů a z toho vyplývajících důsledků. Všechna práva vyhrazena. Žádná část této publikace nesmí být reprodukována ani distribuována žádným způsobem ani prostředkem, ani reprodukována v databázi či na jiném záznamovém prostředku či v jiném systému bez výslovného svolení vydavatele, s výjimkou zveřejnění krátkých částí textu pro potřeby recenzí. Veškeré dotazy týkající se distribuce směřujte na: Zoner Press ZONER software, a.s. Nové sady 18, 602 00 Brno tel.: 532 190 883, fax: 543 257 245 e-mail: knihy@zoner.cz www.zonerpress.cz
ISBN 978-80-7413-076-2
Vzpomínkám na mého milovaného tatínka, Toma Mullinse (1920–2007)
4
Obsah Předmluva
7
Úvod
9
Poděkování
Kapitola 1
13
C# s návrhovými vzory
15
O vzorech
16
O UML
17
Jazyk C# 3.0
19
O příkladech
20
Kapitola 2
Strukturální vzory: Decorator, Proxy a Bridge
21
Vzor Decorator (Dekorátor)
22
Vzor Proxy
36
Vzor Bridge (Most)
51
Srovnání jednotlivých vzorů
62
Kapitola 3
Strukturální vzory: Composite a Flyweight
65
Vzor Composite (Kompozit)
65
Vzor Flyweight (Muší váha)
77
Porovnání vzorů
90
Kapitola 4
Strukturální vzory: Adapter a Faςade
Vzor Adapter (Adaptér)
91 91
Vzor Faςade (Fasáda)
111
Porovnání vzorů
118
Kapitola 5
Vytvářecí vzory: Prototype, Factory Method a Singleton
119
Vzor Prototype (Prototyp)
119
Vzor Factory Method (Tovární metoda)
129
5 Vzor Singleton (Jedináček)
133
Porovnání vzorů
139
Kapitola 6
Vytvářecí vzory: Abstract Factory a Builder
141
Vzor Abstract Factory (Abstraktní továrna)
141
Vzor Builder (Stavitel)
149
Porovnání vzorů
158
Kapitola 7
Vzory chování: Strategy, State a Template Method
159
Vzor Strategy (Strategie)
159
Vzor State (Stav)
169
Vzor Template Method (Šablonová metoda)
180
Porovnání vzorů
184
Kapitola 8
Vzory chování: Chain of Responsibility a Command
187
Vzor Chain of Responsibility (Zřetězení odpovědnosti)
187
Vzor Command (Příkaz)
199
Porovnání vzorů
211
Kapitola 9
Vzory chování: Iterator, Mediator a Observer
213
Vzor Iterator (Iterátor)
213
Vzor Mediator (Prostředník)
226
Vzor Observer (Pozorovatel)
236
Diskuse o vzorech a srovnání
244
Kapitola 10
Vzory chování: Visitor, Interpreter a Memento
247
Vzor Visitor (Návštěvník)
247
Vzor Interpreter (Interpret)
261
Vzor Memento
271
Porovnání vzorů
282
6 Kapitola 11
Budoucnost návrhových vzorů
283
Shrnutí vzorů
283
Co nás čeká v budoucnosti?
287
Závěrečné poznámky
288
Dodatek
289
Příklad vzoru Adapter – CoolBook
289
Příklad vzoru Prototype – Archiv fotografií
294
Příklad vzoru Iterator – Rodokmen
299
Příklad vzoru Observer – Blogy
302
Teoretický příklad vzoru Visitor – Reflexe
307
Příklad vzoru Interpreter – Pravidla kurzu
309
Příklad vzoru Interpreter – Mirrors
314
Seznam použité literatury
319
Rejstřík
321
7
Předmluva Když čelíte nějakému problému, který máte vyřešit, mezi informatiky se často používá základní strategie "rozděl a panuj", kterou lze popsat následovně: Vytvoření modelu konkrétního problému jako množiny několika menších problémů. Samostatné vyřešení každého dílčího problému. Řešení daného problému se získá zkombinováním dílčích výsledků. Redukce složitých problémů na úroveň nastavování stavů několika milionů bitů je to, co děláme neustále. Ale "rozděl a panuj" není jedinou možnou strategií. Lze také použít obecnější přístup: Vytvoření modelu konkrétního problému jako speciálního případu obecnějšího problému. Nějakým způsobem vyřešit obecný problém. Přizpůsobit řešení obecného problému specifickému problému. Návrhové vzory jsou pro ty, kteří mají raději obecnější přístup. Pokud se podíváte na příklady ze širokého spektra softwarových řešení, zjistíte, že ačkoliv detaily se mohou velmi lišit, je často za nimi podobná obecná struktura. (Vyhledávání souboru v adresářové struktuře je v určitém smyslu strukturálně velmi podobné vyhledávání v derivačním stromu.) Návrhové vzory formalizují obecná řešení běžných problémů. Základní příkladem obecného přístupu je samozřejmě návrh a implementace samotných programovacích jazyků. Jak se nástroje pro řešení problémů neustále vyvíjejí, je těžší být obecnější než programovací jazyk (jako je například C#). Při návrhu programovacích jazyků (nebo nových verzí starších programovacích jazyků) přemýšlíme o běžných problémech, kterým opravdoví vývojáři čelí každý den a uvažujeme nad tím, jak vytvořit jazyk, který bude problémy řešit obecně, esteticky a účinným, široce aplikovatelným, způsobem. Chceme vložit nejužitečnější a nejsilnější abstrakce do infrastruktury jazyka tak hluboko, že je sotva zaregistrujete, jako by tam vůbec nebyly. Vzory jako "lokální proměnná", "volání procedury" nebo "cyklus while" jsou natolik součástí vzduchu, který dýcháme, že už je ani nepovažujeme za vzory. Navíc chceme udělat jazyk, ve kterém jsou vzory (které jsou sice užitečné, ale ne zas tak až moc důležité) relativně přímo, čistě a jasně implementovatelné. Třída v programovacím jazyku C# může být označena jako "static", "abstract" nebo "sealed", ale ne jako "singleton". To byla záměrná volba návrhářů jazyka, ačkoli implementace sigleton třídy v C# je relativně jednoduchá. Šedá zóna mezi "nepostradatelným" a "příležitostně užitečným" je právě tím místem, kde leží zajímavé výzvy návrhu. Pozorování návrhových vzorů, které používají vývojáři v C# (a také v dalších jazycích), silně ovlivňuje proces navrhování nových verzí. Zvažte například, jak byste například implementovali vzor Iterator pro lineární seznam (linked list) v C# 1.0. Skončili byste definováním třídy enumerátoru pro reprezentaci pozice v seznamu obsahujícím spoustu nudného a znovu omílaného kódu (což snižuje čitelnost), přičemž výsledné řešení by nebylo moc opakovatelně po-
8 užitelné. Protože pojem "výčet věcí" je snadno aplikovatelný na široké spektrum problémů, bývá zahrnut do mnoha jazyků. V C# 2.0 s příkazem yied return může pro vás kompilátor vygenerovat všechen ten nudný kód a systém generických typů bude iterovat přes kolekci věcí bez ohledu na to, co jsou tyto "věci" vlastně zač. Tohle všechno je jen zdlouhavý způsob jak říct, proč jsem tak nadšený z LINQ (Language Integrated Query) v C# 3.0. Věřím, že iterace přes kolekce věcí je skvělý začátek, ale můžeme udělat mnohem víc. Třídění, filtrování, seskupování, spojování, projekce či transformace dat jsou další důležité operace, které jsou užitečné v každé oblasti. Ať už píšete kompilátor, čtečku XML nebo nějaký bezpečnostní systém pro internetové bankovnictví, je velká šance, že budete potřebovat mnoha způsoby manipulovat s kolekcemi. Odstraněním těchto konceptů z doménově-specifických modelů objektů a jejich vložením přímo do programovacího jazyka pro obecné účely lze vyřešit všelijaké obecnější problémy. Doufám také, že přidání dotazovacích výrazů, lambda výrazů, rozšiřovacích metod, výrazů inicializátoru atd. do C# povede ke zjednodušení implementace všech možný užitečných vlastností. A to je také důvod, proč jsem nadšen knihou C# – návrhové vzory, která se věnuje občas těžko pochopitelnému svět návrhových vzorů ve spojení s C#. Těším se na dobu, až uvidím, jak vývojáři používají tyto nástroje a jaké užitečné vzory budeme moci zabudovat do infrastruktur budoucích jazyků. – Eric Lippert Senior Developer C# Compiler Team Seattle, Washington 30. listopad, 2007
9
Úvod Proč jsem napsala tuto knihu V roce 2002 Microsoft Research hostoval mezinárodní setkání v Cambridge ve Velké Británii, kde představil systém Rotor, který by přinesl C# a .NET i programátorům na jiných platformách, než je Windows. Jakmile jsem se vrátila domů a začala psát software, články a knihu na toto téma, uvědomila jsem si, že jsem byla svědkem opravdové revoluce v programování. Od příchodu Javy v roce 1996 bylo programování nezávislé na platformě: díky Java bytecode mohly programy běžet kdekoliv. Tato nezávislost nicméně rozšířila pouze programy, které byly napsány v Javě. Na druhou stranu – .NET je zcela nezávislý na jazyce, protože umožňuje interakci programů, které byly napsány v různých jazycích, ale zatím jen na platformě Windows. V následujících pěti letech podpořily technologii .NET nejenom nové platformy, jako je třeba Mono (http://www.mono-project.com), ale také nový hardware s podporou čipů od Intelu (na kterých běží Windows). Výsledkem toho všeho je, že .NET nyní běží téměř kdekoliv. S přihlédnutím k těmto faktům lze říci, že znalost jazyka C# je rozhodně přenositelná dovednost. C# se ale jako programovací jazyk neustále zlepšuje a v době psaní této knihy jsme byli svědky posunu směrem k verzi 3.0, která přinesla další zlepšení produktivity a zjednodušení programování. Uvědomila jsem si, že nově ohlášené vlastnosti budou znamenat mnohem pokročilejší úroveň vývoje softwaru a chtěla jsem napsat knihu, která by představila C# 3.0 vývojářům, kteří už mají za sebou nezbytné základy. Jenže jak postupovat, abych v knize mohla představit samotný jazyk a současně čtenářům demonstrovat příklady, které mají svůj základ v reálném světě? A zde přicházejí na scénu návrhové vzory. Návrhové vzory dohromady zapouzdřují osvědčené způsoby pro využívání všelijakých funkcí jazyka. Umožňují programovat na vyšší úrovni a propagují dobré programátorské praktiky. Kolem návrhových vzorů nicméně existuje jistý mýtus neskutečnosti, takže někdo může mít dojem, že se o nich spíše mluví, než aby se používaly v praxi. To jsem ale chtěla změnit, takže jsem vymyslela takové příklady návrhových vzorů, které jsou srozumitelné i běžným programátorům za použití toho nejlepšího programovacího jazyka pro ně – C#. Výsledkem mé práce je tato kniha.
Komu je tato kniha určena Pokud jste programátor, který miluje svůj kód, pro kterého má každý řádek jasný smysl a každý znak své přesné místo, je tato kniha pro vás. Pomůže vám s prací při vytváření správného, elegantního, rozšiřitelného a efektivního kódu. Pokud se soustředíte na kvalitu kódu, rozhodně potřebujete knihu jako C# – návrhové vzory. Znalost návrhových vzorů je rovněž velkým krokem kupředu pro ty, kteří se chtějí vypracovat z běžných programátorů na softwarové inženýry a architekty. Při čtení této knihy získáte následující dovednosti: Programování s návrhovými vzory.
10 Základy UML modelování pro reprezentaci vzorů. Vybírání vzorů vhodných pro dané scénáře a srovnávání alternativních implementací. Používání pokročilých jazykových vlastnosti C# 3.0 k efektivní a elegantní realizaci vzorů. Ačkoli kniha C# – návrhové vzory není psána jako učebnice, může posloužit jako kurz návrhových vzorů nebo programování pro středně pokročilé. Všechny UML diagramy, errata a zdrojové kódy pro vzory popisované v této knize můžete najít na této stránce: http://patterns.cs.up.ac.za/
Znalosti nutné pro práci s touto knihou Tato kniha je určena pro programátory, kteří umí programovat v C# 1.0, a kteří by se rádi dozvěděli více informací o nových vlastnostech tohoto jazyka. Spousta nových vlastností z jazyka C# 3.0 (stejně jako mnoho nových vlastností z C# 2.0) je představeno v příkladech. Tato kniha tudíž může posloužit i jako průvodce programátora světem C#. Kniha od svých čtenářů nepředpokládá žádnou předchozí znalost návrhových vzorů. Pokrývá celou sadu 23 vzorů, které původně navrhli Erich Gamma, Richard Helm, Ralph Johnson a John Vlisside v knize Design Patterns: Element sof Reusable Object-Oriented Software v roce 1994 a nyní formuje běžný základ do vzorů, které se objevují i v mnoha jiných oblastech (jako je bezpečnost, souběžnost či architektonický design). Po přečtení knihy by měl čtenář získat pevné základy o zde popisovaných vzorech.
Jak je tato kniha organizována Po nezbytném úvodu v kapitole 1 přichází 23 základních návrhových vzorů. Každá kapitola probírá dva nebo tři vzory podle toho, že mají podobné použití a mohou být na konci kapitoly vzájemně porovnány. Vzory jsou rozděleny do těchto tří skupin: Strukturální vzory (kapitoly 2–4). Vytvářecí vzory (kapitoly 5–6 ). Vzory chování (kapitoly 7–10). Začneme se strukturálními vzory. V kapitole 2 se budeme zabývat vzory Decorator, Proxy a Bridge. V kapitole 3 si popíšeme vzory Composite a Flyweight. V kapitole 4 vzory Adapter a Faςade. Poté se přesuneme k vytvářecím vzorům – v kapitole 5 objevíme vzory Prototype, Factory Method a Singleton. V kapitole 6 se zaměříme na vzory Abstract Factory a Builder. Poslední čtyři kapitoly se zabývají největší kategorií, což jsou vzory týkající se chování. Kapitola 7 se soustředí na vzory Strategy, State a Template Method. Kapitola 8 popisuje vzory Chain of Responsibility a Command. Kapitola 9 se zaměřuje vzory Iterator, Mediator a Observer. A konečně – poslední kapitola o vzorech se bude věnovat vzorům Visitor, Interpreter a Memento.
C# – návrhové vzory
11
Naše diskuse o každém vzoru se bude skládat z následujících částí: Účel. Krátký popis vzoru a toho, čeho by měl dosáhnout. Ilustrace. Příklad toho, kde by vzor mohl být použit v moderních počítačových systémech ilustrující skutečný kontext s fotografií či diagramem, který pomůže si vzor zapamatovat. Návrh. Popis účastníků vzoru a jejich účelu (vysvětleno v UML diagramu s odkazy zpět k ilustraci). Implementace. Postupné vylepšování vývoje krátkého programu, který ilustruje vzor s použitím pojmů zmíněných v oddíle Návrh. Příklad. Druhý program, který ukazuje příklad vzoru, obvykle v pojmech ilustrativního příkladu, kde se programování odchyluje od přísného lpění na pojmech vzoru. Použití. Popis skutečných scénářů, kde mohou být vzory použity. Tato sekce je zakončena seznamem podmínek, které by měly být splněny, aby mohl být vzor aplikován. Cvičení. Seznam cvičení s různými úrovněmi obtížnosti, který je navržen tak, aby zvýšil vaše porozumění danému vzoru. Na konci každé kapitoly pak naleznete porovnání vzorů, které v ní byly probrány a diskusi o tom, jak zapadají do těch vzoru, které už byly popsány. Vzory byly pečlivě seřazeny tak, aby bylo možné reprezentovat jejich stupňující se vývoj z hlediska používání C# 3.0. Implementace dřívějších vzorů v každém oddíle obvykle vyžaduje trochu něčeho, co není dostupné v C# 1.0, zatímco pozdější vzory mají sofistikovanější implementaci a výhodu v pokročilejších vlastnostech, které jsou dostupné v C# 3.0. Tento přístup umožňuje, aby mohla být daná vlastnost jazyka představena v okamžiku, jakmile se stane relevantním, což je mnohem praktičtější než tyto vlastnosti představovat někde na začátku knihy. Tato kniha rozhodně není průvodcem C# 3.0. Spíše je praktickým průvodcem některými zajímavými vlastnostmi tohoto jazyka. Ačkoliv je kladen důraz na C# 3.0, jsou občas vyzdviženy i vlastnosti C# verze 1.0 a 2.0, o kterých si myslím, že jsou užitečné, ale nejsou moc často používány. V této knize kromě již zmiňovaných věcí naleznete: Obrazové ilustrace vzorů, které vám pomohou se soustředit na význam toho, co může vzor ve skutečnosti provádět. Kvízy, které jsou vztaženy k ilustracím UML diagramů. Jednoduché "teoretické" kódy, které mohou být přizpůsobeny mnoha situacím. Tabulky, které dávají vodítko, kdy si vybrat určitý vzor. Srovnávací tabulky, které ukazují, v čem jsou si vzory podobné a v čem se liší. Seznam výhod, nevýhod a omezení pro každý vzor. Všelijaké výzvy a doplňková cvičení, které pomohou prohloubit vaše znalosti.
12
Co potřebujete pro použití této knihy Pro spuštění programů v této knize potřebujete: Počítač s Windows XP nebo novějším. Kompatibilními platformami jsou jakákoli PC nebo na Intelu založený Mac s virtuálním strojem. Microsoft .NET Framework 3.5. Stahujte z http://www.microsoft.com/downloads. Editor kódu nebo celé vývojové prostředí. Ačkoliv Visual Studio 2008 (nebo novější verze) je ideálním doprovodem k programování v C# 3.0, není nezbytné. Dokumentaci k C# 3.0, kterou naleznete na http://msdn2.microsoft.com/vcsharp.
Konvence použité v této knize Pro lepší přehlednost kniha používá následující typografické zásady: Neproporcionální písmo. Použito pro výpisy kódů, příkazy, nastavení, proměnné, atributy, funk-
ce, typy, třídy, jmenné prostory, metody, vlastnosti, parametry, hodnoty, objekty, události a XML značky. Také se používá pro URL adresy nebo názvy souborů. Neproporcionální písmo tučné. Použito pro zvýraznění částí ve výpisech kódů.
Text tohoto šedého rámečku může obsahovat tip, doporučení, nebo obecnou poznámku.
Sdělte nám svůj názor Jako čtenáři této knihy se stáváte těmi nejdůležitějšími kritiky a komentátory. Vážíme si vašeho názoru a chtěli bychom vědět, co děláme správně, co bychom mohli dělat lépe, ve kterých oblastech bychom měli publikovat, a také vaše další podnětné myšlenky, o které jste ochotni se podělit. Jako odborný redaktor Zoner Press vítám vaše názory. Můžete mi psát – poslat e-mail nebo dopis – a sdělit mi, co se vám v této knize líbilo nebo nelíbilo, stejně tak, co bychom měli udělat, aby naše další knihy byly lepší. Pokud mi napíšete, nezapomeňte, prosím, připojit název knihy, ISBN, jméno autora, vaše jméno, telefon, fax nebo e-mail. Pozorně zhodnotím vaše názory a poskytnu je všem lidem, kteří pracovali na této knize. Prosím, vězte, že nemohu pomoci s technickými problémy, které se týkají obsahu knihy, a že díky velkému množství e-mailů, jež dostávám, nemohu zaručit odpověď na každou zprávu. E-mail: miroslav.kucera@zoner.cz nebo knihy@zoner.cz. Adresa: ZonerPress ZONER software, a.s. Miroslav Kučera Nové sady 18, 602 00 Brno
13
Poděkování Můj první dík patří Johnu Osbornovi, mému editorovi ve vydavatelství O'Reilly, za udržení víry a vydání knihy v termínu. Velmi oceňuji jeho péči a znalosti. Dále Jeffovi Peperovi, který tuto knihu v rychlosti prohlédnul. Mrzí mě, že jsme ji nemohli projít společně. Také děkuji následujícím recenzentům: Eric Lippert, Jim Whitehead, Stefan Gruner a Pierre-Henri Kuaté. Jejich konkrétní poznámky vedly k mnoha revizím a celkově k mnohem lepší knize. Musím také zmínit mé oddělení na University of Pretoria, které mi poskytlo nejnovější technické vybavení. Nesmím opomenout své kolegy, kteří mi dali čas koncentrovat se, když jsem to potřebovala. Dále děkuji Janovi Eloffovi za jeho podporu a přátelství. Musím zmínit i Carla Ghezziho z Politecnico di Milano, který mě laskavě hostil v létě 2007, když už byla napsána většina rukopisu. Moji dřívější studenti Hans Lombard a D-J Miller mi pomohli s několika příklady a já opravdu oceňuji jejich svěžího ducha a věnování se úkolu. Psaní této knihy by bylo mnohem méně zábavné bez neustálé podpory a zájmu mých dobrých přátel, kterými jsou Nigel Horspool, Rob Koenig a Rudolph Vosser. Nikdy přesně nevěděli, kdy bude kniha dokončena, ale ve své podpoře vytrvali, takže kniha byla nakonec úspěšně dokončena. A úplně nakonec děkuji své matce, mým synům a mé rodině, jejichž láska a podpora se mě držela po celou dobu psaní této (a jiných) knihy – děkuji vám moc. – Judith Bishopová Pretoria, Jihoafrická republika Říjen 2007
14
C# – návrhové vzory
65
3 Strukturální vzory: Composite a Flyweight Strukturální vzory Composite (Kompozit) a Flyweight (Muší váha) se týkají systémů, které mají mnoho datových objektů. Vzor Composite je široce aplikovatelný, a při jeho skládání je možné použít i vzor Flyweight. Vzor Flyweight sdílí identické objekty bez vědomí uživatele, aby ušetřil paměť. Tyto vzory ve svých implementacích používají následující vlastnosti jazyka C#: Generické typy (generics). Vlastnosti (properties). Struktury. Indexery. Implicitní typování. Inicializátory. Anonymní typy.
Vzor Composite (Kompozit) Účel Vzor Composite vytváří strukturované hierarchie, což znamená, že s jednotlivými komponentami lze pracovat úplně stejně jako se skupinami komponent. Typické operace na komponentách zahrnují přidávání, odstraňování, zobrazení, vyhledávání a seskupování.
66
Kapitola 3 – Strukturální vzory: Composite a Flyweight
Ilustrace Počítačových aplikací, které se specializují na seskupování dat, existuje velké množství. Stačí se podívat na hudební playlist v iTunes nebo album digitálních fotografií na Flickr.com nebo iPhoto (obrázek 3.1). Položky jsou umístěny v dlouhém seznamu, který je poté doplněn o strukturu.
Obrázek 3.1. Ilustrace vzoru Composite v aplikaci iPhoto. Na obrázku z aplikace iPhoto lze vidět, že existují různé způsoby pro prohlížení importovaných fotek – chronologicky nebo podle názvu. Jedna fotka se může objevit v mnoha albech (například v albech s názvy Last Roll, 2007 a Switzerland). Vytvoření nového alba vede k vytvoření nového objektu Composite, což ale nemá za následek zkopírování fotky. V tomto kontextu musíme zmínit důležitou informací o vzoru Composite – operace, které jsou prováděny na fotografiích a albech, by měly mít stejný název i výsledný efekt, ačkoliv jejich implementace mohou být odlišné. Uživatel by měl být schopen například zobrazit konkrétní fotografii nebo album (které obsahuje fotografie). Operace vedoucí k odstranění fotografie či alba by se rovněž měla jmenovat a chovat stejně.
Návrh Vzor Composite patří mezi ty nejjednodušší. Pracuje se dvěma typy objektů: Components a Composites (kompozity složené z komponent). Oba typy se přizpůsobují společnému rozhraní, které definuje běžné operace. Objekty Composite se skládají z Component, přičemž operace na Composite jsou ve většině případů implementovány zavoláním ekvivalentních operací na objektech Component. Podívejte se na obrázek 3.2 s UML diagramem tohoto vzoru.
C# – návrhové vzory
67
Obrázek 3.2. UML diagram vzoru Composite. Základními účastníky v UML diagramu vzoru Composite jsou: IComponent. Definuje operace pro složené objekty a nějaké standardní chování, které bude aplikovatelné na oba typy objektů. Operation. Operace na objektech, které vyhovují rozhraní IComponent. Component. Implementuje operace, které jsou aplikovatelné na jeden objekt, který již nemůže být dále rozložen. Composite. Implementuje operace, které jsou aplikovatelné na složené objekty. Pro tyto účely využívá lokálně udržovaný seznam komponent a pravděpodobně používá ekvivalentní operace Component. Klient používá pouze rozhraní IComponent, což mu značně zjednodušuje práci.
Kvíz Spojte účastníky vzoru Composite s aplikací iPhoto Abychom otestovali, zdali rozumíte vzoru Composite, zakryjte levý sloupec této tabulky a pokuste se identifikovat účastníky vzoru s položkami z ilustračního příkladu (viz obrázek 3.1) v pravém sloupci. Následně porovnejte své odpovědi s levým sloupcem. IComponent
Viditelná entita v iPhoto
Component
Jedna fotografie
Composite
Album s fotografiemi
Operation
Otevření a prohlížení
Implementace Ačkoliv naše ilustrace se odkazuje na fotky a alba, návrh a implementace vzoru Composite je kompletně nezávislá na druhu spravované komponenty. Stejně dobře bychom mohli pracovat například
68
Kapitola 3 – Strukturální vzory: Composite a Flyweight
se skupinami lidí nebo s portfoliem bankovních účtů. Operace objektu Composite musí iterovat strukturami těchto komponent. Abychom si uvědomili flexibilitu komponenty libovolného typu a spojení mezi listy a Composite, můžeme použít generické typy, což je vlastnost jazyka C#. Vlastnost jazyka C# – Generické typy Generické typy jsou rozšířením systému typů. Znamená to, že struktury, třídy, rozhraní, delegáti a metody mohou být parametrizovány prostřednictvím dalších typů. Například generický typ List<T> je generátor pro typy jako List<String> a List<Person>. Všechny kolekce v knihovně .NET Frameworku, které jsou použity v C#, jsou dostupné v generické formě. Zahrnují Dictionary, Stack, Queue a List a jejich variace. Existují také generické metody jako Sort a BinarySearch. Ty vyžadují, aby typy, se kterými pracují, implementovali rozhraní ICompare, takže porovnání prvků může být prováděno správně. Pro specifikaci generického typu nebo metody použijte generické parametry ve špičatých závorkách, jako <T>, nebo <T, P>. Generické typy mohou být použity pro specifikaci dalších generických prvků (opět s použitím formátu <T>), nebo mohou být použity přímo jako typ (jako například T). Pro konstrukci konkrétního typu nebo metody z jejich generických protějšků stačí doplnit typy každého generického parametru, jako například <string>. Zdroj: Specifikace C#, verze 3.0, září 2007, sekce 10.1.3.
Deklarováním IComponent, Component a Composite jako generické typy vytvoříme implementaci vzoru Composite, která pak může být instanciována pro konkrétní použití. Poznámka. Pro vzor Composite nebudeme vytvářet program, ale jmenný prostor obsahující dvě třídy, Composite a Component , a rozhraní IComponent.
Začneme s rozhraním IComponent a deklarujeme ho jako generický typ s parametrem T. Pak následují všechny předpokládané operace: public interface IComponent <T> { void Add(IComponent <T> c); IComponent <T> Remove(T s); IComponent <T> Find(T s); string Display(int depth); T Item {get; set;} }
Poznámka. Protože typy budou v samostatném jmenném prostoru, musí být deklarovány jako veřejné.
C# – návrhové vzory
141
6 Vytvářecí vzory: Abstract Factory a Builder Vytvářecí vzory zavádí velmi populární koncept v návrhových vzorech: továrny (factory). Továrny jsou třídy, které vytváří instance objektů bez přímého používání podtříd. V kapitole 5 jsme viděli, jak vzor Factory Method (Tovární metoda) může skrýt název třídy před místem, kde je objekt instanciován. Vzor Abstract Factory (Abstraktní továrna) jde o krok dál – vytváří rodiny příbuzných nebo závislých objektů. Vzor Builder (Stavitel) dále rozšiřuje tuto flexibilitu při vytváření složitějších struktur provázaných objektů.
Vzor Abstract Factory (Abstraktní továrna) Účel Tento vzor podporuje vytváření produktů, které existují v rodinách a jsou navrženy tak, aby byly produkovány společně. Abstraktní továrna může být upravena pro konkrétní továrny, z nichž každá může vytvářet různé produkty různých typů a v různých kombinacích. Vzor izoluje definice produktů a názvy jejich tříd od klienta, takže jediný způsob, jak získat jejich instance, je přes továrnu. Z tohoto důvodu mohou být rodiny produktů jednoduše zaměněny nebo aktualizovány bez nutnosti změnit klienta.
Ilustrace Metlou moderní společnosti je velké množství padělků na trhu. Produkty mnoha známých značek jsou kopírovány a prodávány často jako originály. Někdy jsou kopie předem označeny jako repliky, takže zákazník je si vědom toho, co kupuje. Často ale zákazník neví (nebo se o to vůbec nezajímá),
Kapitola 6 – Vytvářecí vzory: Abstract Factory a Builder
142
z jaké továrny zboží vlastně pochází. Obrázek 6.1 ukazuje některé repliky kabelek – vypadají jako kabelky od Gucciho, ale mohly by pocházet z továrny s názvem Poochy ("Půči"; smyšlené jméno).
Obrázek 6.1. Ilustrace vzoru Abstract Factory – replika kabelek od Gucciho (Poochy). Vzor Abstract Factory vytváří podobnou vrstvu neprůhlednosti tím, že schovává název továrny před klientem. Z hlediska programátora to umožňuje zaměňovat továrny, zatímco stále vyrábí zboží v souladu s dohodnutým rozhraním.
Návrh Vzor Abstract Factory má mnoho účastníků, jak je ukázáno v následujícím UML diagramu (viz obrázek 6.2), ale návrh je ve skutečnosti docela jednoduchý. Klient má konkrétní továrnu odpovídající rozhraní AbstractFactory. Skrz něj žádá o objekty produktů (zde typu A a B). Toto jsou ale abstraktní typy produktů. Až konkrétní třídy továren rozhodují o tom, které produkty klient dostane. To systému umožňuje být nezávislý na způsobech, jak jsou produkty vytvářeny a implementovány. Klient se nezajímá o detaily produktu nebo o jejich aktuální názvy tříd – ví pouze to, že je poskytován objekt typu A nebo objekt typu B. Z formálního hlediska jsou účastníci tohoto vzoru tito: AbstractFactory. Rozhraní s operací Create pro každý abstraktní produkt. Factory1, Factory2. Implementace všech vytvářecích operací AbstractFactory. AbstractProduct. Rozhraní pro jednotlivé druhy produktů s vlastními operacemi. ProductA1, ProductA2, ProductB1, ProductB2. Třídy, které implementují rozhraní AbstractProduct a definují objekty produktu tak, aby mohly být vytvořeny odpovídajícími továrnami. Client. Třída, která přistupuje jen k rozhraní AbstractFactory a AbstractProduct. Zajímavý aspekt vzoru Abstract Factory je ten, že celá rodina produktů může být změněna za běhu aplikace. Pokud například klient používá rodinu objektů vytvořených pomocí FactoryA, může instanciovat FactoryB a přepnout na ni. Protože produkty implementují stejné abstraktní rozhraní, jejich operace budou stejné, ale jejich implementace se mohou lišit.
C# – návrhové vzory
143
Obrázek 6.2. UML diagram vzoru Abstract Factory.
Kvíz Spojte účastníky vzoru Abstract Factory s ilustrací repliky kabelky Abychom otestovali, zdali rozumíte vzoru Abstract Factory, zakryjte levý sloupec této tabulky a pokuste se identifikovat účastníky vzoru s položkami z ilustračního příkladu (viz obrázek 6.1) v pravém sloupci. Následně porovnejte své odpovědi s levým sloupcem. Client AbstractFactory
Zákazník kupující kabelku Vrací kabelku
FactoryA
Gucci
FactoryB
Poochy
AbstractProduct
Kabelky
PorductA1
Pravé kabelky
ProductB1
Repliky kabelek