Intent
- Comporre oggetti in strutture ad albero per rappresentare intere gerarchie. Composite permette ai client di trattare oggetti individuali e composizioni di oggetti in modo uniforme.
- Composizione ricorsiva
- “Le directory contengono voci, ognuna delle quali potrebbe essere una directory.”
- 1-to-many “ha un” su per la gerarchia “è un”
Problema
L’applicazione deve manipolare una collezione gerarchica di oggetti “primitivi” e “compositi”. L’elaborazione di un oggetto primitivo è gestita in un modo, e l’elaborazione di un oggetto composito è gestita diversamente.Dover interrogare il “tipo” di ogni oggetto prima di tentare l’elaborazione non è desiderabile.
Discussione
Definire una classe base astratta (Component) che specifica il comportamento che deve essere esercitato uniformemente in tutti gli oggetti primitivi e compositi. Sottoclasse le classi Primitive e Composite dalla classeComponent. Ogni oggetto Composite si “accoppia” solo al tipo astratto Component per gestire i suoi “figli”.
Utilizzare questo schema ogni volta che si hanno “compositi che contengono componenti, ognuno dei quali potrebbe essere un composito”.
I metodi di gestione dei figli dovrebbero essere normalmente definiti nella classe Composite. Sfortunatamente, il desiderio di trattare Primitives e Composites in modo uniforme richiede che i metodi vengano spostati nella classe astratta Component. Vedere la sezione “Opinioni” più avanti per una discussione sui problemi di “sicurezza” contro “trasparenza”.
Struttura
Compositi che contengono Componenti, ognuno dei quali potrebbe essere unComposito.
Menu che contengono voci di menu, ognuno dei quali potrebbe essere un menu.
Gestori di layout GUI a colonne che contengono widget, ognuno dei quali potrebbe essere un gestore di layout GUI a colonne.
Cartelle che contengono file, ognuna delle quali potrebbe essere una cartella.
Contenitori che contengono elementi, ognuno dei quali potrebbe essere un contenitore.
Esempio
Il Composite compone oggetti in strutture ad albero e permette ai client di trattare in modo uniforme oggetti e composizioni individuali. Anche se l’esempio è astratto, le espressioni aritmetiche sono Compositi. Un’espressione aritmetica consiste di un operando, un operatore (+ – * /), e un altro operando. L’operando può essere un numero o un’altra espressione aritmetica. Così, 2 + 3 e (2 + 3) + (4 * 6) sono entrambe espressioni valide.
Lista di controllo
- Assicuratevi che il vostro problema riguardi la rappresentazione di relazioni gerarchiche di “parti intere”.
- Considerate l’euristica, “Contenitori che contengono contenitori, ognuno dei quali potrebbe essere un contenitore”. Per esempio, “Assemblaggi che contengono componenti, ognuno dei quali potrebbe essere un assemblaggio”. Dividete i vostri concetti di dominio in classi contenitore e classi contenitore.
- Create un’interfaccia “minimo comune denominatore” che renda intercambiabili i vostri contenitori e contenitori. Dovrebbe specificare il comportamento che deve essere esercitato uniformemente in tutti gli oggetti contenitori e container.
- Tutte le classi contenitore e containee dichiarano una relazione “is a” all’interfaccia.
- Tutte le classi contenitore dichiarano una relazione uno-a-molti “has a” all’interfaccia.
- Le classi contenitore sfruttano il polimorfismo per delegare ai loro oggetticontainee.
- I metodi di gestione dei figli dovrebbero essere normalmente definiti nella classe Composite. Sfortunatamente, il desiderio di trattare uniformemente gli oggetti Leaf e Composite può richiedere che i metodi siano promossi alla classe astratta Component. Si veda la Gang of Four per una discussione di questi compromessi tra “sicurezza” e “trasparenza”.
Regole di base
- Composite e Decorator hanno diagrammi di struttura simili, riflettendo il fatto che entrambi si basano sulla composizione ricorsiva per organizzare un numero aperto di oggetti.
- Composite può essere attraversato con Iterator. Il visitatore può applicare un’operazione su un Composite. Il Composite potrebbe usare Chain ofResponsibility per permettere ai componenti di accedere alle proprietà globali attraverso i loro genitori. Potrebbe anche usare Decorator per sovrascrivere queste proprietà sulle parti della composizione. Potrebbe usare Observer per legare una struttura di oggetti ad un’altra e State per permettere ad un componente di cambiare il suo comportamento al variare del suo stato.
- Composite può permettervi di comporre un Mediator da pezzi più piccoli attraverso la composizione ricorsiva.
- Decorator è progettato per permettervi di aggiungere responsabilità agli oggetti senza sottoclasse. Il focus di Composite non è sull’abbellimento ma sulla rappresentazione. Questi intenti sono distinti ma complementari.Di conseguenza, Composite e Decorator sono spesso usati di concerto.
- Flyweight è spesso combinato con Composite per implementare leafnodes condivisi.
Opinioni
L’intero punto del pattern Composite è che il Composite può essere trattato atomicamente, proprio come una foglia. Se volete fornire un protocolloIterator, bene, ma penso che questo sia al di fuori del pattern stesso. Il cuore di questo pattern è la capacità di un client di eseguire operazioni su un oggetto senza bisogno di sapere che ci sono molti oggetti all’interno.
Essere in grado di trattare una collezione eterogenea di oggetti atomicamente (o in modo trasparente) richiede che l’interfaccia “gestione dei figli” sia definita alla radice della gerarchia della classe Composite (la classe abstractComponent). Tuttavia, questa scelta costa la sicurezza, perché i clienti potrebbero cercare di fare cose senza senso come aggiungere e rimuovere oggetti dagli oggetti foglia. D’altra parte, se si “progetta per la sicurezza”, l’interfaccia di gestione dei bambini è dichiarata nella classe Composite, e si perde la trasparenza perché foglie e Composite ora hanno interfacce diverse.
Le implementazioni Smalltalk del pattern Composite di solito non hanno l’interfaccia per gestire i componenti nell’interfaccia Component, ma nell’interfaccia Composite. Le implementazioni C++ tendono a metterla nell’interfaccia Component. Questo è un fatto estremamente interessante e su cui rifletto spesso. Posso offrire teorie per spiegarlo, ma nessuno sa con certezza perché sia vero.
Le mie classi Component non sanno che esistono i Compositi. Non forniscono alcun aiuto per navigare nei Compositi, né alcun aiuto per alterare i contenuti di un Composito. Questo perché vorrei che la classe base (e tutti i suoi derivati) fossero riutilizzabili in contesti che non richiedono Compositi. Quando mi viene dato il puntatore di una classe base, se ho assolutamente bisogno di sapere se è un Composite o meno, userò dynamic_cast
per capirlo. Nei casi in cui dynamic_cast
è troppo costoso, userò un Visitor.
Lamentela comune: “se spingo l’interfaccia Composite giù nella classeComposite, come posso enumerare (cioè attraversare) una struttura complessa? La mia risposta è che quando ho comportamenti che si applicano a gerarchie come quella presentata nel pattern Composite, di solito uso Visitor, quindi l’enumerazione non è un problema – il Visitatore sa in ogni caso, esattamente con che tipo di oggetto sta trattando. Il Visitatore non ha bisogno che ogni oggetto fornisca un’interfaccia di enumerazione.
Composite non ti costringe a trattare tutti i componenti come compositi. Vi dice solo di mettere tutte le operazioni che volete trattare “uniformemente” nella classe Component. Se le operazioni add, remove e simili non possono, o non devono, essere trattate uniformemente, allora non mettetele nella classe base Component. Ricordate, a proposito, che il diagramma di struttura di ogni modello non definisce il modello; semplicemente descrive ciò che nella nostra esperienza è una realizzazione comune. Solo perché il diagramma di struttura di Composite mostra le operazioni di gestione dei bambini nella classe base Component non significa che tutte le implementazioni delpattern devono fare lo stesso.
Supporta il nostro sito web gratuito e possiedi l’eBook!
- 22 design pattern e 8 principi spiegati in profondità
- 406 pagine ben strutturate, facili da leggere e senza gergo
- 228 illustrazioni e diagrammi chiari e utili
- Un archivio con esempi di codice in 4 lingue
- Tutti i dispositivi supportati: Formati EPUB/MOBI/PDF
Per saperne di più…