Druhý den jsem poslouchal National Public Radio Car Talk, populární týdenní vysílání, během kterého se volající ptají na svá vozidla. Před každou programovou přestávkou hostitelé show žádají volající, aby vytočili číslo 1-800-CAR-TALK, což odpovídá číslu 1-800-227-8255. První z nich se samozřejmě pamatuje mnohem snáze než druhá, částečně proto, že slova „AUTOMOBIL“ jsou složená: dvě slova, která představují sedm číslic. Lidé obecně považují za jednodušší zacházet s kompozity než s jejich jednotlivými součástmi. Podobně, když vyvíjíte objektově orientovaný software, je často vhodné manipulovat s kompozity, stejně jako manipulujete s jednotlivými komponentami. Tato premisa představuje základní princip kompozitního návrhového vzoru, jehož téma je tématem Java designové vzory splátka.
Složený vzor
Než se ponoříme do kompozitního vzoru, musím nejprve definovat složené objekty: objekty, které obsahují jiné objekty; například může být výkres složen z grafických primitiv, jako jsou čáry, kruhy, obdélníky, text atd.
Vývojáři Java potřebují kompozitní vzor, protože s kompozity musíme často manipulovat přesně stejným způsobem, jako manipulujeme s primitivními objekty. Například grafická primitiva, jako jsou čáry nebo text, musí být nakreslena, přesunuta a změněna jejich velikost. Ale chceme také provést stejnou operaci s kompozity, jako jsou kresby, které jsou složeny z těchto primitiv. V ideálním případě bychom chtěli provádět operace s primitivními objekty i kompozity přesně stejným způsobem, bez rozlišení mezi nimi. Pokud musíme rozlišovat mezi primitivními objekty a kompozity, abychom mohli provádět stejné operace na těchto dvou typech objektů, náš kód by se stal složitějším a obtížněji implementovatelným, udržovatelným a rozšířitelným.
v Designové vzory, autoři popisují kompozitní vzor takto:
Skládejte objekty do stromových struktur, které představují hierarchie celého celku. Kompozitní umožňuje klientům zacházet s jednotlivými objekty a kompozicemi objektů jednotně.Implementace kompozitního vzoru je snadná. Složené třídy rozšiřují základní třídu, která představuje primitivní objekty. Obrázek 1 ukazuje diagram tříd, který ilustruje strukturu složeného vzoru.
Ve schématu tříd na obrázku 1 jsem použil názvy tříd z Návrhový vzor's Diskuse o složeném vzoru: Součástka
představuje základní třídu (nebo případně rozhraní) pro primitivní objekty a Složený
představuje složenou třídu. Například Součástka
třída může představovat základní třídu pro grafická primitiva, zatímco Složený
třída může představovat a Výkres
třída. Obrázek 1 List
třída představuje konkrétní primitivní objekt; například a Čára
třída nebo a Text
třída. The Provoz1 ()
a Provoz2 ()
metody představují metody specifické pro doménu implementované oběma Součástka
a Složený
třídy.
The Složený
třída udržuje sbírku komponent. Typicky, Složený
metody jsou implementovány iterací přes tuto kolekci a vyvoláním příslušné metody pro každou z nich Součástka
ve sbírce. Například a Výkres
třída může implementovat své kreslit()
taková metoda:
// This method is a Composite method public void draw () {// Iterate over the components for (int i = 0; i <getComponentCount (); ++ i) {// Získat odkaz na komponentu a vyvolat její draw metoda Složka složka = getComponent (i); component.draw (); }}
Pro každou metodu implementovanou v Součástka
třída, Složený
třída implementuje metodu se stejným podpisem, který iteruje přes komponenty kompozitu, jak ukazuje kreslit()
výše uvedená metoda.
The Složený
třída rozšiřuje Součástka
třídy, takže můžete předat kompozit metodě, která očekává komponentu; zvažte například následující metodu:
// Tato metoda je implementována ve třídě, která nesouvisí s // komponenty a kompozitní třídy public void repaint (komponentní komponenta) {// komponenta může být složená, ale protože rozšiřuje // třídu komponent, nemusí tato metoda // rozlišovat mezi složkami a složenými složkami.draw (); }
Předchozí metoda je předána komponentě - buď jednoduché komponentě nebo kompozici - a poté vyvolá tuto komponentu kreslit()
metoda. Protože Složený
třída se prodlužuje Součástka
„ překreslit ()
metoda nemusí rozlišovat mezi komponentami a kompozity - jednoduše vyvolá kreslit()
metoda pro komponentu (nebo kompozit).
Diagram třídy kompozitních vzorů na obrázku 1 ilustruje jeden problém se vzorem: musíte rozlišovat mezi komponentami a kompozity, když odkazujete na Součástka
a musíte vyvolat metodu specifickou pro kompozit, například addComponent ()
. Tento požadavek obvykle splníte přidáním metody, například isComposite ()
, do Součástka
třída. Tato metoda se vrací Nepravdivé
pro komponenty a je přepsán v Složený
třída k návratu skutečný
. Navíc musíte také seslat Součástka
odkaz na a Složený
například takto:
... if (component.isComposite ()) {Composite composite = (Composite) component; composite.addComponent (someComponentThatCouldBeAComposite); } ...
Všimněte si, že addComponent ()
metoda je předána a Součástka
odkaz, kterým může být buď primitivní komponenta, nebo kompozit. Protože touto komponentou může být kompozit, můžete komponenty komponovat do stromové struktury, jak naznačuje výše uvedená citace z Designové vzory.
Obrázek 2 ukazuje alternativní implementaci kompozitního vzoru.
Pokud implementujete kompozitní vzor obrázku 2, nemusíte nikdy rozlišovat mezi komponentami a kompozity a nemusíte vrhat Součástka
odkaz na a Složený
instance. Výše uvedený fragment kódu se tedy zmenší na jeden řádek:
... component.addComponent (someComponentThatCouldBeAComposite); ...
Ale pokud Součástka
odkaz v předchozím fragmentu kódu neodkazuje na a Složený
, co by mělo addComponent ()
dělat? To je hlavní bod sporu s implementací kompozitního vzoru na obrázku 2. Protože primitivní komponenty neobsahují další komponenty, přidávání komponenty k jiné komponentě nemá smysl, takže Component.addComponent ()
metoda může buď selhat tiše, nebo vyvolat výjimku. Přidání komponenty k jiné primitivní komponentě se obvykle považuje za chybu, takže vyvolání výjimky je možná nejlepší postup.
Která implementace kompozitního vzoru - ta na obrázku 1 nebo ta na obrázku 2 - tedy funguje nejlépe? To je vždy téma velké debaty mezi implementátory kompozitních vzorů; Designové vzory upřednostňuje implementaci obrázku 2, protože nikdy nemusíte rozlišovat mezi komponentami a kontejnery a nikdy nemusíte provádět cast. Osobně dávám přednost implementaci obrázku 1, protože mám silnou averzi k implementaci metod ve třídě, které pro daný typ objektu nedávají smysl.
Nyní, když rozumíte složenému vzoru a jak ho můžete implementovat, prozkoumejme příklad složeného vzoru s rámcem Apache Struts JavaServer Pages (JSP).
Složený vzor a dlaždice vzpěr
Rámec Apache Struts obsahuje knihovnu značek JSP, známou jako Dlaždice, která vám umožňuje skládat webovou stránku z více JSP. Tiles je vlastně implementace vzoru CompositeView J2EE (Java 2 Platform, Enterprise Edition), který je založen na Designové vzory Složený vzor. Předtím, než probereme důležitost kompozitního vzoru pro knihovnu značek Dlaždice, nejprve si přečtěte důvody pro Dlaždice a jak je používáte. Pokud jste již obeznámeni s dlaždicemi Struts, můžete si prohlédnout následující části a začít číst v části „Použití složeného vzoru s dlaždicemi Struts“.
Poznámka: Další informace o vzoru J2EE CompositeView si můžete přečíst v mém „Snadno použitelných komponentách webové aplikace s kompozitním pohledem“ (JavaWorld, Prosince 2001) článek.
Návrháři často vytvářejí webové stránky se sadou samostatných oblastí; například webová stránka obrázku 3 obsahuje postranní panel, záhlaví, oblast obsahu a zápatí.
Webové stránky často obsahují více webových stránek se stejným rozložením, jako je například rozložení postranního panelu / záhlaví / obsahu / zápatí obrázku 3. Struts Tiles vám umožní znovu použít obsah i rozložení na více webových stránkách. Než budeme diskutovat o tomto opětovném použití, podívejme se, jak je rozložení obrázku 3 tradičně implementováno pouze pomocí HTML.
Implementujte složitá rozvržení ručně
Příklad 1 ukazuje, jak můžete implementovat webovou stránku z obrázku 3 pomocí HTML:
Příklad 1. Složité rozvržení implementované ručně
Ruční implementace složitých rozvržení <% - Jedna tabulka vyloží veškerý obsah pro tuto stránku -%>
|
|
Předchozí JSP má dvě hlavní nevýhody: Za prvé, obsah stránky je vložen do JSP, takže nemůžete znovu použít žádný z nich, i když boční panel, záhlaví a zápatí budou pravděpodobně stejné na mnoha webových stránkách. Zadruhé, rozložení stránky je také vloženo do tohoto JSP, takže jej také nemůžete znovu použít, i když mnoho dalších webových stránek na stejném webu používá stejné rozložení. Můžeme použít opatření k nápravě první nevýhody, jak budu diskutovat dále.
Implementujte komplexní rozvržení pomocí JSP
Příklad 2 ukazuje implementaci webové stránky z obrázku 3, která používá :