Intent

  • Sammensæt objekter i træstrukturer for at repræsentere hele hierarkier. Composite lader klienter behandle individuelle objekter og sammensætninger af objekter ensartet.
  • Rekursiv sammensætning
  • “Directories contain entries, each of which could be a directory.”
  • 1-til-many “has a” up the “is a” hierarchy

Problem

Anvendelse skal manipulere en hierarkisk samling af “primitive “og “sammensatte” objekter. Behandling af et primitivt objekt håndteres på én måde, og behandling af et sammensat objekt håndteres på en anden måde.Det er ikke ønskeligt at skulle spørge om “typen” af hvert objekt, før man forsøger at behandle det.

Diskussion

Der skal defineres en abstrakt basisklasse (Component), som specificerer den adfærd, der skal udøves ensartet på tværs af alle primitive og sammensatte objekter. Underklasserne Primitive og Composite-klasserne underklasses ud fra Component-klassen. Hvert Composite-objekt “kobler” sig kun til den abstrakte type Component, da det administrerer sine “børn”.

Brug dette mønster, når du har “composites, der indeholder komponenter, som hver især kan være en composite”.

Børnehåndteringsmetoder bør normalt defineres i Composite-klassen. Desværre kræver ønsket om at behandle Primitives og Composites ensartet, at disse metoder flyttes til den abstrakte Component-klasse. Se afsnittet “Udtalelser” nedenfor for en diskussion af “sikkerhed” versus “gennemsigtighed”.

Struktur

Composites, der indeholder Components, som hver især kan være enComposite.

Menuer, der indeholder menupunkter, som hver især kan være en menu.

Row-column GUI-layout managers, der indeholder widgets, som hver især kan være en row-column GUI-layout manager.

Kataloger, der indeholder filer, som hver især kan være en mappe.

Containere, der indeholder elementer, som hver især kan være en container.

Eksempel

Composite sammensætter objekter i træstrukturer og lader klienten behandle individuelle objekter og sammensætninger ensartet. Selv om eksemplet er abstrakt, er aritmetiske udtryk Composites. Et aritmetisk udtryk består af en operand, en operand (+ – * /) og en anden operand. Operanden kan være et tal eller et andet aritmetisk udtryk. Således er 2 + 3 og (2 + 3) + (4 * 6) begge gyldige udtryk.

Checkliste

  1. Sørg for, at dit problem handler om at repræsentere “whole-part”-hierarkiske relationer.
  2. Overvej heuristikken: “Containers that contain containees,each of which could be a container.” For eksempel: “Samlinger, der indeholder komponenter, som hver især kan være en samling.” Opdel dine domænekoncepter i containerklasser og containerklasser.
  3. Skab en “laveste fællesnævner”-grænseflade, der gør dine containere og containere indbyrdes udskiftelige. Den bør specificere den adfærd, der skal udøves ensartet på tværs af alle containee- og containerobjekter.
  4. Alle container- og containee-klasser erklærer et “is a”-forhold til grænsefladen.
  5. Alle containerklasser erklærer et en-til-mange “has a”-forhold til grænsefladen.
  6. Containerklasser udnytter polymorfisme til at delegere til derescontainee-objekter.
  7. Børnehåndteringsmetoder bør normalt defineres i Composite-klassen. Desværre kan ønsket om at behandle Leaf- og Composite-objekter ensartede kræve, at disse metoder skal overføres til den abstrakte Component-klasse. Se Gang of Four for en diskussion af disse afvejninger mellem “sikkerhed” og “gennemsigtighed”.

Tommelfingerregler

  • Composite og Decorator har lignende strukturdiagrammer, hvilket afspejler det faktum, at begge er afhængige af rekursiv komposition til at organisere et ubegrænset antal objekter.
  • Composite kan gennemløbes med Iterator. Visitor kan anvende en operation over en Composite. Composite kan bruge Chain ofResponsibility til at lade komponenter få adgang til globale egenskaber gennem deres forælder. Den kan også bruge Decorator til at tilsidesætte disse egenskaber på dele af sammensætningen. Den kan bruge Observer til at binde en objektstruktur til en anden og State til at lade en komponent ændre sin adfærd, når dens tilstand ændres.
  • Composite kan lade dig sammensætte en Mediator ud af mindre stykker gennem rekursiv sammensætning.
  • D Decorator er designet til at lade dig tilføje ansvarsområder til objekter uden at underklassere dem. Composite’s fokus er ikke på forskønnelse, men på repræsentation. Disse hensigter er forskellige, men komplementære, og derfor bruges Composite og Decorator ofte sammen.
  • Flyweight kombineres ofte med Composite for at implementere delte bladknuder.

Opfattelser

Hele pointen med Composite-mønstret er, at Composite kan behandles atomisk, ligesom et blad. Hvis du ønsker at levere en Iterator-protokol, er det fint, men jeg mener, at det ligger uden for selve mønstret. Kernen i dette mønster er muligheden for, at en klient kan udføre operationer på et objekt uden at skulle vide, at der er mange objekter indeni.

At kunne behandle en heterogen samling af objekter atomisk (eller gennemsigtigt) kræver, at grænsefladen “child management” defineres ved roden af Composite-klassehierarkiet (den abstrakte komponentklasse). Dette valg koster imidlertid sikkerhed, fordi klienter kan forsøge at gøre meningsløse ting som f.eks. at tilføje og fjerne objekter fra bladobjekter. Hvis man derimod “designer med henblik på sikkerhed”, deklareres grænsefladen til forvaltning af børn i Composite-klassen, og man mister gennemsigtighed, fordi blade og Composites nu har forskellige grænseflader.

Smalltalk-implementationer af Composite-mønstret har normalt ikke grænsefladen til forvaltning af komponenterne i Component-grænsefladen, men i Composite-grænsefladen. C++-implementeringer har en tendens til at placere det i Component-interfacet. Dette er en yderst interessant kendsgerning, som jeg ofte tænker over. Jeg kan komme med teorier til at forklare det, men ingen ved med sikkerhed, hvorfor det er sandt.

Mine komponentklasser ved ikke, at der findes Composites. De giver ingen hjælp til at navigere i Composites og heller ingen hjælp til at ændre indholdet af en Composite. Det skyldes, at jeg gerne vil have, at basisklassen (og alle dens afledninger) skal kunne genbruges i sammenhænge, der ikke kræver Composites. Hvis jeg har brug for at vide, om en basisklasse er en Composite eller ej, bruger jeg dynamic_cast til at finde ud af det, når jeg får en pegepind til en basisklasse. I de tilfælde, hvor dynamic_cast er for dyrt, vil jeg bruge en Visitor.

Kommelig klage: “Hvis jeg skubber Composite-grænsefladen ned iComposite-klassen, hvordan skal jeg så opregne (dvs. gennemløbe) en kompleks struktur?” Mit svar er, at når jeg har adfærd, der gælder forhierarkier som det, der præsenteres i Composite-mønstret, bruger jeg typisk Visitor, så opregning er ikke et problem – Visitor ved i hvert enkelt tilfælde præcis, hvilken type objekt den har med at gøre. Visitor har ikke brug for, at hvert objekt skal levere en opregningsgrænseflade.

Composite tvinger dig ikke til at behandle alle Components som Composites. Den fortæller dig blot, at du skal placere alle operationer, som du ønsker at behandle “ensartet”, i Component-klassen. Hvis tilføjelse, fjernelse og lignende operationer ikke kan eller må behandles ensartet, skal de ikke placeres i Component-basisklassen. Husk i øvrigt, at hvert mønsters strukturdiagram ikke definerer mønsteret; det viser blot, hvad der efter vores erfaring er en almindelig realisering af det. Bare fordi Composites strukturdiagram viser child management-operationer i Component-baseklassen, betyder det ikke, at alle implementeringer af mønstret skal gøre det samme.

Støt vores gratis hjemmeside og få e-bogen i din e-bog!

    22 designmønstre og 8 principper forklaret i dybden 406 velstrukturerede, letlæselige sider uden jargon

  • 228 klare og nyttige illustrationer og diagrammer
  • Et arkiv med kodeeksempler på 4 sprog
  • Alle enheder understøttes: Alle enheder: EPUB/MOBI/PDF-formater

Læs mere…

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.