Forleden hørte jeg på National Public Radio Car Talk, en populær ukentlig sending der innringere stiller spørsmål om kjøretøyene sine. Før hver programpause ber programets verter innringere ringe 1-800-CAR-TALK, som tilsvarer 1-800-227-8255. Selvfølgelig viser førstnevnte seg mye lettere å huske enn sistnevnte, delvis fordi ordene "CAR TALK" er en kompositt: to ord som representerer syv sifre. Mennesker synes generelt det er lettere å håndtere kompositter, i stedet for deres individuelle komponenter. På samme måte, når du utvikler objektorientert programvare, er det ofte praktisk å manipulere kompositter akkurat som du manipulerer individuelle komponenter. Denne forutsetningen representerer det grunnleggende prinsippet for det sammensatte designmønsteret, temaet for dette Java Design Patterns avdrag.
Komposittmønsteret
Før vi dykker inn i det sammensatte mønsteret, må jeg først definere sammensatte objekter: gjenstander som inneholder andre gjenstander; for eksempel kan en tegning være sammensatt av grafiske primitiver, som linjer, sirkler, rektangler, tekst og så videre.
Java-utviklere trenger det sammensatte mønsteret fordi vi ofte må manipulere kompositter nøyaktig på samme måte som vi manipulerer primitive objekter. For eksempel må grafiske primitiver som linjer eller tekst tegnes, flyttes og endres. Men vi vil også utføre den samme operasjonen på kompositter, som tegninger, som er sammensatt av disse primitivene. Ideelt sett vil vi utføre operasjoner på både primitive gjenstander og kompositter på nøyaktig samme måte, uten å skille mellom de to. Hvis vi må skille mellom primitive objekter og kompositter for å utføre de samme operasjonene på de to typene objekter, ville koden vår blitt mer kompleks og vanskeligere å implementere, vedlikeholde og utvide.
I Design mønstrebeskriver forfatterne det sammensatte mønsteret slik:
Komponer objekter i trestrukturer for å representere hierarkier fra hele deler. Composite lar kunder behandle individuelle objekter og komposisjoner av objekter jevnt.Det er enkelt å implementere komposittmønsteret. Sammensatte klasser utvider en basisklasse som representerer primitive objekter. Figur 1 viser et klassediagram som illustrerer strukturen til det sammensatte mønsteret.
I figur 1s klassediagram brukte jeg klassenavn fra Design mønster's Sammensatt mønsterdiskusjon: Komponent
representerer en basisklasse (eller muligens et grensesnitt) for primitive objekter, og Sammensatte
representerer en sammensatt klasse. For eksempel Komponent
klasse kan representere en basisklasse for grafiske primitiver, mens Sammensatte
klasse kan representere en Tegning
klasse. Figur 1 Blad
klasse representerer et konkret primitivt objekt; for eksempel en Linje
klasse eller a Tekst
klasse. De Operasjon1 ()
og Operasjon2 ()
metoder representerer domenespesifikke metoder implementert av begge Komponent
og Sammensatte
klasser.
De Sammensatte
klasse vedlikeholder en samling av komponenter. Typisk, Sammensatte
metodene er implementert ved å gjenta over samlingen og påkalle den riktige metoden for hver Komponent
i samlingen. For eksempel, a Tegning
klasse kan implementere sin tegne()
metode som denne:
// Denne metoden er en sammensetningsmetode public void draw () {// Iterer over komponentene for (int i = 0; i <getComponentCount (); ++ i) {// Få en referanse til komponenten og påkalle tegningen metode Komponentkomponent = getComponent (i); component.draw (); }}
For hver metode implementert i Komponent
klasse, den Sammensatte
klasse implementerer en metode med samme signatur som gjentas over komposittkomponentene, som illustrert av tegne()
metoden som er oppført ovenfor.
De Sammensatte
klasse utvider Komponent
klasse, slik at du kan overføre en kompositt til en metode som forventer en komponent; Tenk for eksempel på følgende metode:
// Denne metoden er implementert i en klasse som ikke er relatert til // Component and Composite class public void repaint (Component component) {// Komponenten kan være en kompositt, men siden den utvides // Component-klassen, trenger denne metoden ikke // skille mellom komponenter og kompositter component.draw (); }
Den foregående metoden er sendt en komponent - enten en enkel komponent eller en kompositt - så påkaller den komponenten tegne()
metode. Fordi det Sammensatte
klasse utvider Komponent
, den male på nytt ()
metoden trenger ikke å skille mellom komponenter og kompositter - den påkaller ganske enkelt tegne()
metode for komponenten (eller kompositt).
Figur 1s sammensatte mønster klassediagram illustrerer ett problem med mønsteret: du må skille mellom komponenter og kompositter når du refererer til en Komponent
, og du må påberope en kompositt-spesifikk metode, for eksempel addComponent ()
. Du oppfyller vanligvis dette kravet ved å legge til en metode, for eksempel isComposite ()
, til Komponent
klasse. Den metoden kommer tilbake falsk
for komponenter og overstyres i Sammensatte
klasse å komme tilbake ekte
. I tillegg må du også kaste Komponent
henvisning til a Sammensatte
eksempel, slik:
... hvis (component.isComposite ()) {Composite composite = (Composite) komponent; composite.addComponent (someComponentThatCouldBeAComposite); } ...
Legg merke til at addComponent ()
metoden er bestått a Komponent
referanse, som enten kan være en primitiv komponent eller en kompositt. Fordi komponenten kan være en kompositt, kan du komponere komponentene i en trestruktur, som angitt av det nevnte sitatet fra Design mønstre.
Figur 2 viser en alternativ implementering av sammensatt mønster.
Hvis du implementerer figur 2s sammensatte mønster, trenger du aldri å skille mellom komponenter og kompositter, og du trenger ikke å kaste en Komponent
henvisning til a Sammensatte
forekomst. Så kodefragmentet som er oppført ovenfor, reduseres til en enkelt linje:
... component.addComponent (someComponentThatCouldBeAComposite); ...
Men hvis den Komponent
referanse i forrige kodefragment refererer ikke til a Sammensatte
, hva skal addComponent ()
gjøre? Det er et viktig stridspunkt med figur 2s implementering av sammensatt mønster. Fordi primitive komponenter ikke inneholder andre komponenter, gir det ingen mening å legge til en komponent i en annen komponent Component.addComponent ()
metoden kan enten mislykkes lydløst eller kaste et unntak. Vanligvis betraktes det som en feil å legge til en komponent i en annen primitiv komponent, så å kaste et unntak er kanskje den beste fremgangsmåten.
Så hvilken implementering av sammensatt mønster - den i figur 1 eller den i figur 2 - fungerer best? Det er alltid et tema for god debatt blant implementatorer av sammensatte mønstre; Design mønstre foretrekker figur 2-implementeringen fordi du aldri trenger å skille mellom komponenter og containere, og du trenger aldri å utføre en rollebesetning. Personlig foretrekker jeg implementering av figur 1, fordi jeg har en sterk aversjon mot å implementere metoder i en klasse som ikke gir mening for den objekttypen.
Nå som du forstår det sammensatte mønsteret og hvordan du kan implementere det, la oss undersøke et eksempel på et sammensatt mønster med Apache Struts JavaServer Pages (JSP) rammeverk.
Komposittmønsteret og Struts Tiles
Apache Struts-rammeverket inkluderer et JSP-tagbibliotek, kjent som Tiles, som lar deg komponere en webside fra flere JSPer. Tiles er faktisk en implementering av J2EE (Java 2 Platform, Enterprise Edition) CompositeView-mønster, basert på Design mønstre Sammensatt mønster. Før vi diskuterer det sammensatte mønsterets relevans for fliser-tagbiblioteket, la oss først gjennomgå begrunnelsen for fliser, og hvordan du bruker den. Hvis du allerede er kjent med Struts Tiles, kan du gå gjennom følgende seksjoner og begynne å lese på "Bruk det sammensatte mønsteret med Struts Tiles."
Merk: Du kan lese mer om J2EE CompositeView-mønsteret i "Web Application Components Made Easy with Composite View" (JavaWorld, Desember 2001) artikkel.
Designere lager ofte nettsider med et sett med diskrete regioner; for eksempel omfatter figur 3s webside en sidefelt, topptekst, innholdsregion og bunntekst.
Nettsteder inkluderer ofte flere nettsider med identiske oppsett, for eksempel figur 3s sidefelt / topptekst / innhold / bunntekstoppsett. Struts Tiles lar deg bruke både innhold og layout på flere websider. Før vi diskuterer gjenbruk, la oss se hvordan figur 3s layout tradisjonelt er implementert med HTML alene.
Implementere komplekse oppsett for hånd
Eksempel 1 viser hvordan du kan implementere figur 3s webside med HTML:
Eksempel 1. Et komplekst oppsett implementert for hånd
Implementering av komplekse oppsett for hånd <% - Én tabell viser alt innholdet på denne siden -%>
|
|
Den forrige JSP har to store ulemper: For det første er innholdet på siden innebygd i JSP, så du kan ikke gjenbruke noe av det, selv om sidefelt, topptekst og bunntekst sannsynligvis vil være det samme på mange nettsider. For det andre er sidens layout også innebygd i den JSP, så du kan heller ikke bruke den igjen, selv om mange andre websider på samme nettsted bruker samme layout. Vi kan bruke handling for å avhjelpe den første ulempen, som jeg diskuterer neste.
Implementere komplekse oppsett med JSP inkluderer
Eksempel 2 viser en implementering av figur 3s webside som bruker :