Home > GOF Pattern, java > GoF Patterns: Builder

GoF Patterns: Builder

10 gennaio 2011

Translate in English with Google Translate
In questo articolo tratterò il pattern Builder.

Motivazione
Si tratta di un pattern creazionale basato su oggetti e viene utilizzato per creare un oggetto senza doverne conoscere i suoi dettagli implementativi. Questo pattern consente di utilizzare un Client che non debba essere a conoscenza dei passi necessari al fine della creazione di un oggetto ma tali passaggi vengono delegati ad un Director che sa cosa e come fare.

Partecipanti e Struttura
Questo pattern è composto dai seguenti partecipanti:

  1. Director: costruisce un oggetto partendo dall’interfaccia Builer
  2. Builder: specifica una interfaccia atta alla creazione del Product
  3. ConcreteBuilder: costruisce il Product in base ai metodi definiti nel Builder
  4. Product: rappresenta l’oggetto complesso da costruire
Builder Pattern

Builder Pattern

Conseguenze

Tale pattern presenta i seguenti vantaggi/svantaggi:

  1. consente di cambiare la rappresentazione interna del prodotto: il Builder non conosce la rappresentazione interna del prodotto che può essere cambiata semplicemente costruendo un nuovo Builder.
  2. isolamento tra Builder: ogni Builder è indipendente dall’altro pertanto è possibile aumentare la modularità.
  3. controllo accurato del processo di creazione: la creazione avviene step-by-step e questo consente di stabilire passo dopo passo cosa effettuare.

Implementazione

Come esempio ho preso spunto dall’esempio presente su wikipedia in merito al Builder Pattern che ho un pò modificato.
L’ipotesi dell’esempio è quella di un cliente di una pizzeria che ordina una pizza margherita al cameriere e questi incarica il pizzaiolo “specializzato”. In questo caso il cameriere funge da Director mentre il pizzaiolo addetto alla margherita funge da ConcreteBuilder e si occupa di preparare la pizza in base agli ingredienti giusti. La pizza è ovviamente il Product.

Vediamo come si presenta il pattern in UML in base all’esempio:

Builder Pattern

Builder Pattern

Vediamo come si presenta la classe Cliente:

package patterns.builder;
public class Cliente {
    public static void main(String[] args) {
        Cameriere cameriere= new Cameriere();
        CuocoPizza cuocoPizza = new CuocoPizzaMargherita();
        cameriere.setCuocoPizza(cuocoPizza);
        cameriere.creaPizza();
        Pizza pizza = cameriere.getPizza();
        System.out.println( "Ingredienti: " + pizza.getIngredienti() );
    }
}

Ecco la classe Cameriere che avrà il compito di passare l’ordinazione al pizzaiolo:

package patterns.builder;
public class Cameriere {
    private CuocoPizza cuocoPizza;
    public void setCuocoPizza(CuocoPizza cuocoPizza) {
        this.cuocoPizza = cuocoPizza;
    }
    public void creaPizza() {
        cuocoPizza.creaPizza();
        cuocoPizza.ingredienti();
    }
    public Pizza getPizza() {
        return cuocoPizza.getPizza();
    }
}

Queste sono le classi che si occupano di creare la pizza:

package patterns.builder;
public abstract class CuocoPizza {
    protected Pizza pizza;
    public Pizza getPizza() {
        return pizza;
    }
    public void creaPizza() {
        pizza = new Pizza();
    }
    public abstract void ingredienti();
}
package patterns.builder;
public class CuocoPizzaCapricciosa  extends CuocoPizza {
    @Override
    public void ingredienti() {
        pizza.setIngredienti("pomodoro, mozzarella, funghi, prosciutto, olive");
    }
}
package patterns.builder;
public class CuocoPizzaMargherita extends CuocoPizza {
    @Override
    public void ingredienti() {
        pizza.setIngredienti("pomodoro, mozzarella");
    }
}

Mentre questa classe rappresenta il prodotto da creare: la pizza.

package patterns.builder;
public class Pizza {
    private String ingredienti = "";
    public void setIngredienti(String ingredienti) {
        this.ingredienti = ingredienti;
    }
    public String getIngredienti() {
        return ingredienti;
    }
}

Eseguiamo la classe Cliente e visualizziamo l’output:

$JAVA_HOME/bin/java patterns.builder.Cliente
Ingredienti: pomodoro, mozzarella

In questo caso l’ordine del cliente è di una pizza margherita e vengono visualizzati di seguito gli ingredienti usati.

Categorie:GOF Pattern, java
  1. 6 maggio 2014 alle 1:49 PM

    Buongiorno, noto che la pizza margherita viene consegnata in formato standard al cliente che l’ha ordinata. Nel caso il cliente volesse fare delle aggiunte(es. più mozzarella) sarebbe possibile definire tale informazione rispettando il pattern o tale aggiunta violerebbe in qualche modo l’integrità del pattern Builder?

    • 7 maggio 2014 alle 12:45 AM

      Il pattern Builder è un pattern creazionale pertanto l’oggetto creato detiene tutti i dati. Mentre invece utilizzando altri pattern strutturali, come il pattern Decorator oppure il pattern Composite, l’oggetto viene “arricchito” ossia vengono creati nuovi oggetti che fanno da contenitori, come una matrioska, e tali oggetti espongono delle proprietà/comportamenti aggiuntivi che danno l’impressione che l’oggetto sia stato modificato mentre invece si tratta solo di aggregazione di oggetti.
      Pertanto se l’obiettivo è quello di avere come prodotto finito un oggetto Pizza che detenga tutti gli ingredienti al proprio interno, ed anche la Mozzarella in più, allora è necessario utilizzare un pattern creazionale come il Builder e prevedere in fase di costruzione l’aggiunta di altri ingredienti.
      Mentre invece se l’obiettivo è quello di ottenere una Pizza “decorata” con aggiunta di altri ingredienti, come la mozzarella, e tale decisione non è stata prevista in sede iniziale, allora il Decorator può essere l’unica possibilità, sia in quanto “decora” senza modificare l’oggetto pizza e sia in quanto non richiede la riapertura del Builder. In questo secondo esempio il Decorator interviene dopo la creazione del Builder e non durante, leggi anche i commenti sui due pattern su stackoverflow: http://stackoverflow.com/questions/4768349/builder-vs-decorator-pattern
      Seguendo questi 2 approcci, ti riporto degli esempi di implementazione: nel primo caso intervenendo in fase di creazione dell’oggetto mentre nel secondo caso “decorandolo” a run-time, spero che siano chiari.

      Primo approccio: Integrazione del Builder

      package patterns.builder;
      
      import patterns.decorator.Mozzarella;
      import patterns.decorator.ProdottoPizza;
      
      public class Cliente {
          public static void main(String[] args) {
              Cameriere cameriere= new Cameriere();
              CuocoPizza cuocoPizza = new CuocoPizzaMargherita();
              cameriere.setCuocoPizza(cuocoPizza);
              
              //Costruzione: modificando l'oggetto pizza
              cameriere.addIngredientiAggiunti("mozzarella");
      
              cameriere.creaPizza();
              Pizza pizza = cameriere.getPizza();
              System.out.println( "Ingredienti: " + pizza.getIngredienti() );
          }
      }
      
      package patterns.builder;
      public class Cameriere {
          private CuocoPizza cuocoPizza;
          public void setCuocoPizza(CuocoPizza cuocoPizza) {
              this.cuocoPizza = cuocoPizza;
          }
          public void creaPizza() {
              cuocoPizza.creaPizza();
              cuocoPizza.ingredientiBase();
              //comunicare al cuoco gli ingredienti aggiuntivi
              cuocoPizza.ingredientiAggiunti(ingredientiAggiunti);
          }
          public Pizza getPizza() {
              return cuocoPizza.getPizza();
          }
      
          //mantenere le info degli ingredienti
          private String ingredientiAggiunti;
          public void addIngredientiAggiunti(String ingredientiAggiunti) {
              this.ingredientiAggiunti = ingredientiAggiunti;
          }
      }
      
      package patterns.builder;
      public abstract class CuocoPizza {
          protected Pizza pizza;
          public Pizza getPizza() {
              return pizza;
          }
          public void creaPizza() {
              pizza = new Pizza();
          }
          public abstract void ingredientiBase();
          //gestire gli ingredienti per i cuochi
          public abstract void ingredientiAggiunti(String ingredientiAggiunti);
      }
      
      package patterns.builder;
      
      public class CuocoPizzaMargherita extends CuocoPizza {
          
          @Override
          public void ingredientiBase() {
              pizza.setIngredienti("pomodoro, mozzarella");
          }
      
          @Override
          public void ingredientiAggiunti(String ingredientiAggiunti) {
              if (ingredientiAggiunti!=null) {
                  pizza.setIngredienti(pizza.getIngredienti() + ", " + ingredientiAggiunti);
              }
          }
      }
      

      Output:

      Ingredienti: pomodoro, mozzarella, mozzarella
      

      Secondo approccio: Affiancamento del Decorator

      package patterns.builder;
      
      import patterns.decorator.Mozzarella;
      import patterns.decorator.ProdottoPizza;
      
      public class Cliente {
          public static void main(String[] args) {
              Cameriere cameriere= new Cameriere();
              CuocoPizza cuocoPizza = new CuocoPizzaMargherita();
              cameriere.setCuocoPizza(cuocoPizza);
              cameriere.creaPizza();
              Pizza pizza = cameriere.getPizza();
              System.out.println( "Ingredienti: " + pizza.getIngredienti() );
              
              //Decorazione: senza modificare l'oggetto pizza 
              ProdottoPizza prodotto = new Mozzarella( pizza );
              System.out.println( "Ingredienti: " + prodotto.getIngredienti() );
          }
      }
      
      package patterns.builder;
      
      import patterns.decorator.ProdottoPizza;
      //In questo caso la Pizza implementa l'interfaccia ProdottoPizza
      public class Pizza implements ProdottoPizza {
          private String ingredienti = "";    
      
          public void setIngredienti(String ingredienti) {
              this.ingredienti = ingredienti;
          }    
      
          public String getIngredienti() {
              return ingredienti;
          }
      
      }
      
      package patterns.decorator;
      
      public interface ProdottoPizza {
          
          public String getIngredienti();
      
      }
      
      package patterns.decorator;
      
      public abstract class IngredientiAggiuntivi implements ProdottoPizza {
      
          ProdottoPizza prodottoPizza;
          public IngredientiAggiuntivi(ProdottoPizza prodottoPizza){
              this.prodottoPizza = prodottoPizza;
          }
      }
      
      package patterns.decorator;
      
      public class Mozzarella extends IngredientiAggiuntivi{
      
          public Mozzarella(ProdottoPizza prodottoPizza) {
              super(prodottoPizza);
          }
      
          @Override
          public String getIngredienti() {
              return prodottoPizza.getIngredienti() + ", mozzarella";
          }
          
      }
      

      Output:

      Ingredienti: pomodoro, mozzarella, mozzarella
      
  1. No trackbacks yet.
I commenti sono chiusi.
%d blogger cliccano Mi Piace per questo: