Pattern Builder

1. Introduzione

Il design pattern Builder, separa la costruzione di un oggetto complesso dalla sua rappresentazione cosicché il processo di costruzione stesso possa creare diverse rappresentazioni.

L’algoritmo per la creazione di un oggetto complesso è indipendente dalle varie parti che costituiscono l’oggetto e da come vengono assemblate.

Ciò ha l’effetto immediato di rendere più semplice la classe, permettendo a una classe builder separata di focalizzarsi sulla corretta costruzione di un’istanza e lasciando che la classe originale si concentri sul funzionamento degli oggetti. Questo è particolarmente utile quando volete assicurarvi che un oggetto sia valido prima di istanziarlo, e non volete che la logica di controllo appaia nei costruttori degli oggetti. Un builder permette anche di costruire un oggetto passo-passo, cosa che si può verificare quando si fa il parsing di un testo o si ottengono i parametri da un’interfaccia interattiva.

Nella versione di esempio ho un unico oggetto il cui comportamento cambia in base ai suoi attributi.

2. Struttura di un Builder

  • (Abstract) Builder: specifica l‘interfaccia astratta che crea le parti dell’oggetto Product.
  • ConcreteBuilder: costruisce e assembla le parti del Product implementando l’interfaccia Builder; definisce e tiene traccia della rappresentazione che crea.
  • Director: costruisce un oggetto utilizzando l’interfaccia Builder.
  • Product: rappresenta l’oggetto complesso e include le classi che definiscono le parti che lo compongono, includendo le interfacce per assemblare le parti nel risultato finale.
pattern-builder-example

3. Funzionamento

  • Il Client crea un oggetto Director e lo configura con gli oggetti Builder desiderati.
  • Il Director notifica al Builder se una parte del prodotto deve essere costruita, il Builder riceve le richieste dal Director e aggiunge le parti al prodotto.
  • Il Client riceve il prodotto dal Builder.

3.1 Differenze con l’Abstract Factory

  • Il Builder si focalizza sulla costruzione di un oggetto complesso step by step. Abstract Factory enfatizza una famiglia di oggetti (sia semplici che complessi).
  • Il Builder restituisce il prodotto come passo finale del processo di creazione, mentre per quanto riguarda l’Abstract Factory, il prodotto viene ritornato immediatamente.

3.2 Analogie con altri pattern

Il pattern creazionale più semplice è il Factory, generalmente si parte sempre da quello. Se il pattern genera troppe sottoclassi spesso conviene migrare verso l’Abstract Factory, il Prototype o il Builder, che sono più flessibili ma più complessi.

4. Esempio

4.1 Product

Classe Product, il prodotto complesso. In questo caso i suoi attributi sono stringhe, ma potrebbero essere anche oggetti più complessi.
Notare come il costruttore sia vuoto e di come gli attributi vengano creati con i setter, caratteristica tipica del pattern builder, che separa la costruzione di un oggetto dalla sua logica interna.

class Pizza
{
  // Attributi
  string dough;
  string sauce;
  string topping;

  // Costruttore (vuoto)
  public Pizza() {}

  //Setter
  public void SetDough( string d){ dough = d;}
  public void SetSauce( string s){ sauce = s;}
  public void SetTopping( string t){ topping = t;}
  // ... logiche complesse ...
}

4.2 Builder

Il builder è una classe astratta che indica come bisogna creare l’oggetto Product.
In questo caso quindi indica che per creare una pizza vi è la necessità di avere i motodi BuildDough, BuildSauce e BuildTopping.
Fornisce inoltre i metodi per ottenere il product e per crearne uno nuovo.

abstract class PizzaBuilder
{
  // possiede il Product come attributo
  protected Pizza pizza;
  // Costruttore (vuoto)
  public PizzaBuilder(){}

  //Getter per il product
  public Pizza GetPizza(){ return pizza; }
  // Metodo (concreto)
  public void CreateNewPizza() { pizza = new Pizza(); }

  // Metodi (astratti) di costruzione del Product
  public abstract void BuildDough();
  public abstract void BuildSauce();
  public abstract void BuildTopping();
}

4.3 Concrete Builder

Implementa l’Abstract Builder implementando solo i metodi astratti della costruzione dell’oggetto.

class HawaiianPizzaBuilder : PizzaBuilder
{
  // override dei metodi del Parent astratto
  public override void BuildDough() { pizza.SetDough("cross"); }
  public override void BuildSauce() { pizza.SetSauce("mild"); }
  public override void BuildTopping() { pizza.SetTopping("ham+pineapple"); }
}
class SpicyPizzaBuilder : PizzaBuilder
{
  public override void BuildDough() { pizza.SetDough("pan baked"); }
  public override void BuildSauce() { pizza.SetSauce("hot"); }
  public override void BuildTopping() { pizza.SetTopping("pepparoni+salami"); }
}

4.4 Director

Il Director è colui che effettivamente si occupa della creazione dell’oggetto Product a partire da un Builder.
Avrà quindi sempre un setter che gli imposta il tipo di Builder da utilizzare, un metodo che fornisce l’oggetto Product e un metodo che lo costruisce utilizzando i metodi del Builder.
Notare come il Director sia l’unico oggetto a sapere come si costruisce il Product, è lui a chiamare i metodi BuildDough, BuildSauce e BuildTopping.

class Waiter
{
  // Attributo e setter per l'AbstractBuilder
  private PizzaBuilder pizzaBuilder;
  public void SetPizzaBuilder (PizzaBuilder pb) { pizzaBuilder = pb; }

  //Fornisce l'oggetto Product
  public Pizza GetPizza() { return pizzaBuilder.GetPizza(); }

  // Costruisce l'oggetto Product utilizzando i metodi del Builder
  public void ConstructPizza()
  {
    pizzaBuilder.CreateNewPizza();
    pizzaBuilder.BuildDough();
    pizzaBuilder.BuildSauce();
    pizzaBuilder.BuildTopping();
  }
}

4.5 Client

Di seguito una invocazione dall’esterno del metodo Builder.
Notare che questo metodo offre i seguenti vantaggi per il client esterno:
– Il client non deve conoscere gli attributi e i componenti dell’oggetto complesso Pizza
– Il client deve conoscere solo le tipologie possibili di pizza, in questo caso che ne esiste una tipologia Hawaian e una tipologia Spicy. Non conosce di cosa queste pizze sono composte.
– Il client deve solo creare un Director e dirgli il tipo di Product da creare, al resto ci pensa lui
– Alla fine del procedimento il Director mi fornisce una istanza dell’oggetto Product di cui il client non conosce alcunchè.
– Se un giorno volessi creare un nuovo tipo di pizza basta creare un nuovo oggetto ConcreteBuilder, il resto rimane immutato

// Istanzio un nuovo Director
Waiter waiter = new Waiter();

// Istanzio n ConcreteBuilder
PizzaBuilder hawaiianPizzaBuilder = new HawaiianPizzaBuilder();
PizzaBuilder spicyPizzaBuilder = new SpicyPizzaBuilder();

// Il Director ha bisogno di un concreteBuilder per operare
waiter.SetPizzaBuilder ( hawaiianPizzaBuilder );

// Il Director può ora costriure il Product
waiter.ConstructPizza();

// Una volta costruito, ottengo l'oggetto finale.
Pizza pizza = waiter.GetPizza();

Vantaggi

Il vantaggio di utilizzare il pattern builder è la possibilità di costruire un oggetto step by step. Questo quindi è quanto prescritto dal principio di singola responsabilità e, genericamente, dai principi di buona programmazione.

Dove approfondire

Per approfondire consiglio assolutamente la lettura di Head First Design Pattern, un libro imprendiscindibile per chiunque voglia migliorarsi come programmatore.

Indice

Share
Ultimi articoli
Join

Newsletter

Nessuno spam, solo articoli interessanti ;)

Focus

Post correlati

semaphoreslim

SmaphoreSlim 101

SemaphoreSlim è una classe che permette la sincronizzazione di n thread che hanno una risorsa (scarsa) condivisa limitandone l’uso ad un numero massimo.

interlocked

Interlocked 101

La sincronizzazione dei thread è un elemento fondamentale nella programmazione asincrona, ne ho infatti parlato in vari post. La soluzione più versatile è sicuramente utilizzare

event

Come testare gli eventi

Testare che degli eventi siano stato effettivamente lanciati in C# non è immediato. Tipicamente è possibile testare che un evento venga lanciato aspettando un ManualResetEvent

Codice Pragmatico

Contatti

Per informazioni, dubbi o consulenze non esitate a contattarmi.

Lascia un messaggio

Ricevi le ultime news

Iscrivi alla newsletter

Solo articoli interessanti, promesso ;)