Druhý deň som počúval národné verejné rádio Car Talk, populárne týždenné vysielanie, počas ktorého sa volajúci pýtajú na svoje vozidlá. Pred každou programovou prestávkou hostitelia prehliadky požiadajú volajúcich, aby vytočili číslo 1-800-CAR-TALK, čo zodpovedá číslu 1-800-227-8255. Samozrejme, prvé sa pamätá oveľa ľahšie ako druhé, čiastočne preto, že slová „CAR TALK“ sú zložené: dve slová, ktoré predstavujú sedem číslic. Ľudia majú vo všeobecnosti jednoduchšie zaobchádzať s kompozitmi, ako s ich jednotlivými súčasťami. Rovnako tak pri vývoji objektovo orientovaného softvéru je často vhodné manipulovať s kompozitmi rovnako, ako manipulujete s jednotlivými komponentmi. Táto premisa predstavuje základný princíp kompozitného návrhového vzoru, o ktorom sa pojednáva Dizajnové vzory Java splátka.
Kompozitný vzor
Predtým, ako sa ponoríme do kompozitného vzoru, musím najskôr definovať zložené objekty: predmety, ktoré obsahujú ďalšie predmety; napríklad môže byť kresba zložená z grafických primitív, ako sú čiary, kruhy, obdĺžniky, text atď.
Vývojári v jazyku Java potrebujú kompozitný vzor, pretože s kompozitmi musíme často manipulovať úplne rovnako ako s primitívnymi objektmi. Napríklad musia byť nakreslené, presunuté a zmenené veľkosť základných grafických prvkov, ako sú čiary alebo text. Rovnakú operáciu však chceme vykonať aj pri kompozitoch, ako sú napríklad kresby, ktoré sú zložené z týchto primitívov. V ideálnom prípade by sme chceli vykonávať operácie s primitívnymi objektmi aj s kompozitmi presne rovnakým spôsobom, bez rozdielu medzi týmito dvoma. Ak musíme rozlišovať medzi primitívnymi objektmi a kompozitmi, aby sme mohli vykonať rovnaké operácie na týchto dvoch typoch objektov, náš kód by sa stal zložitejším a náročnejším na implementáciu, údržbu a rozšírenie.
V Dizajnové vzory, autori popisujú kompozitný vzor takto:
Skladajte objekty do stromových štruktúr, ktoré reprezentujú hierarchie jednotlivých častí. Kompozit umožňuje klientom zaobchádzať s jednotlivými objektmi a kompozíciami objektov jednotne.Implementácia kompozitného vzoru je jednoduchá. Zložené triedy rozširujú základnú triedu, ktorá predstavuje primitívne objekty. Obrázok 1 zobrazuje diagram tried, ktorý ilustruje štruktúru zloženého vzoru.
V schéme tried na obrázku 1 som použil názvy tried z Dizajnový vzor 's Diskusia o zloženom vzore: Komponent
predstavuje základnú triedu (alebo prípadne rozhranie) pre primitívne objekty a Zložený
predstavuje zloženú triedu. Napríklad Komponent
trieda môže predstavovať základnú triedu pre grafické primitívy, zatiaľ čo Zložený
trieda môže predstavovať a Kreslenie
trieda. Obrázok 1 List
trieda predstavuje konkrétny primitívny objekt; napríklad a Riadok
trieda alebo a Text
trieda. The Prevádzka1 ()
a Prevádzka2 ()
metódy predstavujú metódy špecifické pre doménu implementované oboma Komponent
a Zložený
triedy.
The Zložený
trieda udržuje zbierku komponentov. Typicky Zložený
metódy sa implementujú iteráciou nad touto kolekciou a vyvolaním príslušnej metódy pre každú z nich Komponent
v zbierke. Napríklad a Kreslenie
trieda môže implementovať svoje kresliť ()
metóda, ako je táto:
// Táto metóda je kompozitná metóda public void draw () {// Iterácia nad komponentami pre (int i = 0; i <getComponentCount (); ++ i) {// Získať odkaz na komponent a vyvolať jeho draw metóda Component component = getComponent (i); component.draw (); }}
Pre každú metódu implementovanú v Komponent
triedy, Zložený
trieda implementuje metódu s rovnakým podpisom, ktorá iteruje nad zložkami kompozitu, ako to ilustruje kresliť ()
metóda uvedená vyššie.
The Zložený
trieda rozširuje Komponent
triedy, takže môžete zloženému zložiť metódu, ktorá očakáva komponent; zvážte napríklad nasledujúcu metódu:
// Táto metóda je implementovaná v triede, ktorá nesúvisí s // triedami komponentov a kompozitov public void repaint (komponentná zložka) {// Komponent môže byť zložený, ale pretože rozširuje // triedu komponentu, táto metóda nemusí // rozlišovať medzi komponentmi a kompozitmi component.draw (); }
Predchádzajúcou metódou sa odovzdá komponent - buď jednoduchý komponent, alebo kompozit - a potom sa tento komponent vyvolá kresliť ()
metóda. Pretože Zložený
trieda predlžuje Komponent
, premaľovať ()
metóda nemusí rozlišovať medzi komponentmi a kompozitmi - jednoducho vyvolá kresliť ()
metóda pre komponent (alebo kompozit).
Diagram triedy kompozitných vzorov na obrázku 1 ilustruje jeden problém so vzorom: musíte rozlišovať medzi komponentmi a kompozitmi, keď odkazujete na Komponent
, a musíte sa odvolať na špecifickú metódu zloženia, ako je napr addComponent ()
. Túto požiadavku obvykle spĺňate pridaním metódy, ako napr isComposite ()
, do Komponent
trieda. Táto metóda sa vráti nepravdivé
pre komponenty a je prepísaný v Zložený
triedy na návrat pravda
. Ďalej musíte tiež obsadiť Komponent
odkaz na a Zložený
napríklad takto:
... if (component.isComposite ()) {Composite composite = (Kompozitný) komponent; composite.addComponent (someComponentThatCouldBeAComposite); } ...
Všimnite si, že addComponent ()
metóda je odovzdaná a Komponent
odkaz, ktorým môže byť buď primitívny komponent alebo kompozit. Pretože touto zložkou môže byť kompozit, môžete ju zložiť do stromovej štruktúry, ako naznačuje vyššie uvedený citát z Dizajnové vzory.
Obrázok 2 zobrazuje alternatívnu implementáciu zloženého vzoru.
Ak implementujete kompozitný vzor na obrázku 2, nemusíte nikdy rozlišovať medzi komponentmi a kompozitmi a nemusíte vrhať Komponent
odkaz na a Zložený
inštancia. Vyššie uvedený fragment kódu sa teda zmenší na jeden riadok:
... component.addComponent (someComponentThatCouldBeAComposite); ...
Ale ak Komponent
odkaz v predchádzajúcom fragmente kódu neodkazuje na a Zložený
, čo by malo addComponent ()
robiť? To je hlavný bod sporu pri implementácii kompozitného vzoru na obrázku 2. Pretože primitívne komponenty neobsahujú ďalšie komponenty, nemá pridanie komponentu k inému komponentu zmysel, takže Component.addComponent ()
metóda môže buď zlyhať potichu, alebo môže spôsobiť výnimku. Pridanie komponentu k inému primitívnemu komponentu sa zvyčajne považuje za chybu, takže uplatnenie výnimky je možno najlepším postupom.
Ktorá implementácia zloženého vzoru - teda tá na obrázku 1 alebo tá na obrázku 2 - funguje najlepšie? To je vždy téma veľkej debaty medzi implementátormi kompozitných vzorov; Dizajnové vzory uprednostňuje implementáciu obrázku 2, pretože nikdy nemusíte rozlišovať medzi komponentmi a kontajnermi a už nikdy nemusíte vykonávať cast. Osobne uprednostňujem implementáciu obrázku 1, pretože mám veľkú averziu voči implementácii metód v triede, ktoré pre daný typ objektu nedávajú zmysel.
Teraz, keď rozumiete zloženému vzoru a tomu, ako ho môžete implementovať, poďme preskúmať príklad zloženého vzoru v rámci JSP (Apache Struts JavaServer Pages).
Kompozitný vzor a dlaždice Struts
Rámec Apache Struts obsahuje knižnicu značiek JSP, známu ako Dlaždice, ktorá vám umožní zostaviť webovú stránku z viacerých JSP. Tiles je vlastne implementácia vzoru CompositeView J2EE (Java 2 Platform, Enterprise Edition), ktorý je založený na Dizajnové vzory Zložený vzor. Predtým, ako prediskutujeme relevantnosť kompozitného vzoru pre knižnicu značiek Tiles, najskôr si prečítajme dôvody Tiles a toho, ako ich používate. Ak už dlaždice Struts poznáte, môžete si prečítať nasledujúce časti a začať čítať v časti „Použite zložený vzor s dlaždicami Struts“.
Poznámka: Viac informácií o vzore J2EE CompositeView si môžete prečítať v mojom dokumente „Webové komponenty ľahko použiteľné pomocou kompozitného zobrazenia“ (JavaWorld, Decembra 2001) článok.
Dizajnéri často vytvárajú webové stránky so súborom samostatných oblastí; napríklad webová stránka obrázka 3 obsahuje bočný panel, hlavičku, oblasť obsahu a pätu.
Webové stránky často obsahujú viac webových stránok s rovnakým rozložením, ako je napríklad rozloženie bočného panela / hlavičky / obsahu / päty na obrázku 3. Struts Tiles vám umožní opätovne použiť obsah aj rozloženie na viacerých webových stránkach. Predtým, ako prediskutujeme toto opätovné použitie, pozrime sa, ako sa rozloženie obrázka 3 tradične implementuje iba pomocou HTML.
Komplexné rozloženia implementujte ručne
Príklad 1 ukazuje, ako môžete implementovať webovú stránku obrázka 3 s HTML:
Príklad 1. Komplexné usporiadanie implementované ručne
Ručná implementácia zložitých rozložení <% - Jedna tabuľka rozloží všetok obsah pre túto stránku -%>
|
|
Predchádzajúci JSP má dve hlavné nevýhody: Po prvé, obsah stránky je vložený do JSP, takže nemôžete znova použiť nič z toho, aj keď je pravdepodobné, že bočný panel, hlavička a päta budú rovnaké na mnohých webových stránkach. Po druhé, rozloženie stránky je tiež vložené do tohto JSP, takže ju rovnako nemôžete znova použiť, aj keď mnoho iných webových stránok na tej istej webovej stránke používa rovnaké rozloženie. Môžeme použiť kroky na nápravu prvej nevýhody, ako budem diskutovať ďalej.
Implementujte komplexné rozloženia pomocou JSP
Príklad 2 ukazuje implementáciu webovej stránky z obrázku 3, ktorá používa :