Introduzione
Il design pattern Builder, come tutti i pattern creazionali, separa la costruzione di un oggetto complesso dalla sua rappresentazione cosicché il processo di costruzione stesso possa creare diverse rappresentazioni.
In particolare questo pattern è utilizzato quando l'oggetto deve essere creato mediante vari step ed è il builder che mantiene lo stato di tutti gli step.
Alla fine della creazione l’oggetto viene ritornato.
Il pattern builder si può riconoscere in una classe che ha un solo metodo di creazione e vari metodi per configurare l’oggetto creato.
Un’esempio classico è la classe StringBuilder
, che permette di costruire una stringa mediante vari step e fornirla solo alla fine.
Rapporto con altri pattern creazionali
Il pattern creazionale più semplice è il Pattern Factory e generalmente si parte sempre da quello. Se il pattern genera troppe sottoclassi spesso conviene migrare verso l’Abstract Factory
, il Pattern Prototype o il Builder
, che sono più flessibili ma leggermente più complessi.
Differenze con il pattern Factory
- Il
Builder
si focalizza sulla costruzione di un oggetto complesso step by step. Pattern Factory fornisce una determinato oggetto a partire da una famiglia in base ad un parametro in ingresso. - Anche il
Builder
può creare vari sottoclassi in base al parametro in ingresso ma con una maggiore granularità nella loro creazione: esempio se ilFactory
ritorna come istanze diCar
Honda
eFord
, ilBuilder
potrebbe ritornare unaHonda 4 cilindri
e unaHonda a 6 cilindri
in base alla configurazione. Il patternFactory
può essere visto come una versione semplificata delBuilder
. - Il
Builder
restituisce il prodotto come passo finale del processo di creazione, mentre per quanto riguarda ilfactory
oAbstract Factory
, il prodotto viene ritornato immediatamente.
Struttura
(Abstract) Builder
: è la classe astratta che viene utilizzata dall’esterno per creare le parti dell’oggettoProduct
. Ha questo come variabile di stato e vari metodi, da chiamare all’esterno, per assemblarlo.ConcreteBuilder
: costruisce e assembla le parti delProduct
implementando l’interfacciaBuilder
. Aggiorna l’istanza della classeProduct
della classe padre.Director
: costruisce un oggetto utilizzando l’interfacciaBuilder
. Lo scopo del Director è eseguire i passi della costruzione dell’oggetto in un particolare ordine, o solo alcuni passi, in base ai metodi chiamati dall’esterno. Questa classe non è obbligatoria nel pattern in quanto il client può fare la stessa operazione, ha senso solo se ho varie configurazioni diverse dello stesso oggetto da costruire.Product
: rappresenta l’oggetto complesso che voglio costruire.
Funzionamento
- Il
Client
crea un oggettoDirector
ne imposta l’oggettoBuilder
; - Il
Director
notifica alBuilder
se una parte del prodotto deve essere costruita in base alle richieste delClient
, ilBuilder
riceve le richieste dalDirector
e aggiunge le parti al prodotto. - Il
Client
riceve il prodotto dalBuilder
e lo utilizza.
Esempio
Product
Classe Product
, il prodotto complesso. In questo caso ho una lista di stringa come attributo ma nel mondo reale potrebbero essere anche oggetti più complessi.
Notare come il costruttore sia vuoto e di come gli attributi vengano creati con un metodo (o dei setter), caratteristica tipica del pattern builder, che separa la costruzione di un oggetto dalla sua logica interna.
AbstractBuilder
Il Builder
è una classe astratta che indica come bisogna creare l’oggetto Product
.
In questo caso quindi indica che per creare l’oggetto Product posso crearne la parte A, B o C (o tutte insieme) tramite i metodi BuildPartA
, BuildPartB
e BuildPartC
.
Fornisce inoltre i metodi per ottenere il product e per crearne uno nuovo.
Concrete Builder
Implementa l’Abstract Builder
implementando solo i metodi astratti della costruzione dell’oggetto.
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 BuildPartA
, BuildPartB
e BuildPartC
.
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 complessoProduct
- Il client deve conoscere solo le tipologie possibili Product, in questo caso che ne esiste una tipologia full optionale
BuildFullFeaturedProduct
e una tipologia base (BuildMinimalViableProduct
). Non conosce di cosa queste pizze sono composte. - Il client deve solo creare un
Director
e dirgli il tipo diProduct
da creare, al resto ci pensa lui - Alla fine del procedimento il
Director
mi fornisce una istanza dell’oggettoProduct
di cui il client non conosce alcunchè. - Se un giorno volessi creare un nuovo tipo di
Product
basta creare un nuovo oggettoConcreteBuilder
, il resto rimane immutato
Nel mio esempio il client fornisce:
Product minimal with standard builder:
Product parts: Standard PartA1
Product full optional with standard builder:
Product parts: Standard PartA1, Standard PartB1, Standard PartC1
Product minimal with custom builder:
Product parts: Custom PartA1
Product full optional with custom builder:
Product parts: Custom PartA1, Custom PartB1, Custom PartC1
Product without builder:
Product parts: Standard PartA1, Standard PartC1-
Rider Live template
Allego il live template per utilizzare questo pattern in Rider o Visual Studio con ReSharper.
Transclude of Builder-Rider-live-template.txt