Composite

Da Wikipedia, l'enciclopedia libera.
bussola Disambiguazione – Se stai cercando le piante della famiglia delle Compositae della classe dei dicotiledoni, vedi Asteraceae.

Nella programmazione ad oggetti, il Composite è uno dei pattern fondamentali, definiti originariamente dalla gang of four.

Questo pattern permette di trattare un gruppo di oggetti come se fossero l'istanza di un oggetto singolo. Il design pattern Composite organizza gli oggetti in una struttura ad albero, nella quale i nodi sono delle composite e le foglie sono oggetti semplici.

È utilizzato per dare la possibilità ai client di manipolare oggetti singoli e composizioni in modo uniforme.

Struttura di una composite[modifica | modifica sorgente]

Struttura del pattern Composite
  • Client: manipola gli oggetti attraverso l'interfaccia Component.
  • Component: dichiara l'interfaccia per gli oggetti nella composizione, per l'accesso e la manipolazione di questi, imposta un comportamento di default per l'interfaccia comune a tutte le classi e può definire un'interfaccia per l'accesso al padre del componente e la implementa se è appropriata.
  • Composite: definisce il comportamento per i componenti aventi figli, salva i figli e implementa le operazioni ad essi connesse nell'interfaccia Component.
  • Leaf: definisce il comportamento degli oggetti primitivi, cioè delle foglie.

Scopo[modifica | modifica sorgente]

Quando i programmatori trattano dati strutturati ad albero devono spesso discriminare se stanno visitando un nodo o una foglia. Questa differenza è una possibile fonte di complessità per il codice e, se non trattata a dovere, rende il programma facilmente soggetto a errori. La soluzione è adottare un'interfaccia che permetta di trattare oggetti complessi e primitivi in modo uniforme. Nella programmazione orientata agli oggetti un Composite è un oggetto (per esempio una figura geometrica) progettato per composizione di uno o più oggetti simili (sempre per rimanere sullo stesso esempio: una figura geometrica come un trapezio può essere vista a sua volta come formata da due triangoli rettangoli e un rettangolo) che offrono tutti le medesime funzionalità. Questa caratteristica è conosciuta anche come relazione "has-a" tra gli oggetti. Il concetto fondamentale è che il programmatore manipola ogni oggetto dell'insieme dato nello stesso modo: sia esso un "raggruppamento" o un oggetto singolo. Le operazioni che possono essere effettuate sul Composite hanno spesso un denominatore comune, per esempio: se il programmatore deve visualizzare a schermo un insieme di figure potrebbe essergli utile definirne il ridimensionamento in modo tale da avere lo stesso effetto (in senso lato) del ridimensionamento di una singola figura.

Quando usarlo[modifica | modifica sorgente]

Il Composite può essere usato quando i client dovrebbero ignorare la differenza tra oggetti composti e oggetti singoli. Se durante lo sviluppo i programmatori scoprono che stanno usando più oggetti nello stesso modo, e spesso il codice per gestirli è molto simile, il Composite rappresenta una buona scelta di rifattorizzazione: in questa situazione, è meno complesso trattare oggetti primitivi e composti in modo omogeneo.

Funzionamento[modifica | modifica sorgente]

Attraverso l'interfaccia Component, il Client interagisce con gli oggetti della composite. Se l'oggetto desiderato è una Leaf, la richiesta è processata direttamente; altrimenti, se è una Composite, viene rimandata ai figli cercando di svolgere le operazioni prima e dopo del rimando.

In questo modo, si semplifica il Client, si creano delle gerarchie di classi, si semplifica l'aggiunta di nuovi componenti, anche se il design diventa troppo generale.

Esempi[modifica | modifica sorgente]

Il seguente esempio, scritto in Java, implementa una classe grafica che può essere o un'ellisse o una composizione di vari ellissi (o di qualsiasi altra classe che implementa Graphic, nel seguente esempio abbiamo solo Ellisse). Ogni figura può essere stampata. In forma algebrica,

       Graphic = ellipse | GraphicList
       GraphicList = empty | Graphic GraphicList

Può essere estesa fino ad implementare altre forme (rettangolo, ecc.) e metodi (traslare, ecc.).

List = empty_list | atom List | List List

import java.util.List;
import java.util.ArrayList;
 
interface Graphic {
 
    //Stampa il grafico.
    public void print();
 
}
 
class CompositeGraphic implements Graphic {
 
    //Collezione di grafici figli.
    private List<Graphic> mChildGraphics = new ArrayList<Graphic>();
 
    //Stampa il grafico.
    public void print() {
        for (Graphic graphic : mChildGraphics) {
            graphic.print();
        }
    }
 
    //Aggiunge il grafico alla composizione.
    public void add(Graphic graphic) {
        mChildGraphics.add(graphic);
    }
 
    //Rimuove il grafico dalla composizione.
    public void remove(Graphic graphic) {
        mChildGraphics.remove(graphic);
    }
 
}
 
class Ellisse implements Graphic {
 
    //Stampa il grafico.
    public void print() {
        System.out.println("Ellisse");
    }
 
}
 
public class Program {
 
    public static void main(String[] args) {
        //Inizializza tre ellissi
        Ellisse ellisse1 = new Ellisse();
        Ellisse ellisse2 = new Ellisse();
        Ellisse ellisse3 = new Ellisse();
        Ellisse ellisse4 = new Ellisse();
 
        //Inizializza tre grafici composti
        CompositeGraphic graphic = new CompositeGraphic();
        CompositeGraphic graphic1 = new CompositeGraphic();
        CompositeGraphic graphic2 = new CompositeGraphic();
 
        //Compone i grafici
        graphic1.add(ellisse1);
        graphic1.add(ellisse2);
        graphic1.add(ellisse3);
 
        graphic2.add(ellisse4);
 
        graphic.add(graphic1);
        graphic.add(graphic2);
 
        //Stampa i grafici completi (quattro volte la stringa "Ellisse").
        graphic.print();
    }
}

Esempio C++

#include <iostream>
#include <vector>
 
using namespace std;
 
class Component { public: virtual void traverse() = 0; };
 
class Primitive : public Component {
   int value;
public:
   Primitive( int val ) { value = val; }
   void traverse()      { cout « value « "  "; }
};
 
class Composite : public Component {
   vector<Component*> children;
   int                value;
public:
   Composite( int val )     { value = val; }
   void add( Component* c ) { children.push_back( c ); }
   void traverse() {
      cout « value « "  ";
      for (int i=0; i < children.size(); i++)
          children[i]->traverse();
}  };
 
class Row : public Composite { public:     // Due diversi tipi di classi
   Row( int val ) : Composite( val ) { }   // "container".  La maggior parte del
   void traverse() {                       // codice è nella classe base Composite
      cout « "Row";                       
      Composite::traverse();
}  };
 
class Column : public Composite { public:
   Column( int val ) : Composite( val ) { }
   void traverse() {
      cout « "Col";
      Composite::traverse();
}  };
 
main() {
      Row    first( 1 );                     // Row1
      Column second( 2 );                    //   |
      Column third( 3 );                     //   +-- Col2
      Row    fourth( 4 );                    //   |     |
      Row    fifth( 5 );                     //   |     +-- 7
      first.add( &second );                  //   +-- Col3
      first.add( &third  );                  //   |     |
      third.add( &fourth );                  //   |     +-- Row4
      third.add( &fifth  );                  //   |     |     |
      first.add(  &Primitive( 6 ) );         //   |     |     +-- 9
      second.add( &Primitive( 7 ) );         //   |     +-- Row5
      third.add(  &Primitive( 8 ) );         //   |     |     |
      fourth.add( &Primitive( 9 ) );         //   |     |     +-- 10
      fifth.add(  &Primitive(10 ) );         //   |     +-- 8
      first.traverse();  cout « '\n';       //   +-- 6
}

Voci correlate[modifica | modifica sorgente]

Altri progetti[modifica | modifica sorgente]

Collegamenti esterni[modifica | modifica sorgente]