Il modello di progettazione del costruttore consente di passare i parametri? Se sì, qual è il modo giusto per raggiungerlo?
Panoramica
Il pattern del builder nasconde i dettagli del costrutto dell'oggetto, posiziona due livelli di astrazione tra il prodotto e il client: builder e director
- builder è l'interfaccia per la costruzione del prodotto, l'idea è polimorfismo: gli stessi metodi sono usati per impostare diversi prodotti
- director separa il cliente dal processo di costruzione
- Il pattern Builder è pensato per prodotti complessi, quindi l'interfaccia del builder potrebbe essere grande:
setPart1()
,setPart2()
, ...setPart100()
- director ha un'interfaccia piccola e semplice:
constructProduct()
,getProduct()
- lo stesso director potrebbe utilizzare diversi builder di calcestruzzo per diversi prodotti in calcestruzzo
- ma anche la stessa interfaccia builder può essere utilizzata da diversi amministratori
Che cosa succede se alcune proprietà del prodotto non possono essere nascoste dal client.
Esempio di problema
Supponiamo di avere il prodotto complesso come il set di computer.
class ComputerSet {
private:
string procName;
string hddName;
int hddCapacity;
public:
ComputerSet(string p) : procName(p) {}
void setHdd(string name, int capacity);
};
void ComputerSet::setHdd(string name, int capacity) {
hddName = name;
hddCapacity = capacity;
}
In realtà, non è così complesso. Il prodotto ha solo 3 parti da impostare: procName
, hddName
, hddCapacity
. Il motivo è di rendere piccolo l'esempio. Diciamo che il set del computer potrebbe avere molti componenti.
Dividere la parte HDD in due parti ( nome e capacità ) è stata fatta per la semplicità dell'esempio. Hanno una natura diversa: hddName
è costante costitutiva , hddCapacity
è builder-variant .
Supponiamo che ci sia il prodotto concreto: set di computer con procName="Amdel"
e hddName="Orange"
. Questi dettagli potrebbero essere nascosti dal client. Il cliente:
- crea l'istanza di Director
- crea l'istanza di AmdelBuilder
- passa AmbelBuilder al direttore
- utilizza l'interfaccia di Director per ottenere il prodotto
- non conosce le stringhe "Amdel" e "Orange"
Ma ci potrebbero essere molte capacità di HDD da scegliere: 250, 500, 750, 1000, 1500, 2000.
Un modo è creare il costruttore per ogni capacità: AmdelBuilder250, AmdelBuilder500, AmdelBuilder750, AmdelBuilder1000, AmdelBuilder1500, AmdelBuilder2000.
Che ne dici di progettare solo un AmbelBuilder
per ogni capacità e passare il parametro hddCapacity
appena prima della creazione del prodotto?
Come passare il parametro:
-
contenerlo all'interno del builder come sua proprietà (membro dei dati); due tipi di impostazioni sia dal livello client
- impostato con il costruttore (una volta per istanza)
- impostato con il setter (impostazione di più valori per singola istanza)
-
rendi l'argomento del metodo builder , deve essere passato dal livello director
- diventa l'argomento del metodo di costruzione del director
- lo contengono all'interno del director come sua proprietà (membro dei dati)
Primo approccio
Il Builder memorizza hddCapacity
come proprio campo dati
class Builder {
protected:
ComputerSet *cs;
int hddCapacity;
public:
Builder(int capacity = 250) : hddCapacity(capacity) { }
virtual void createComputerSet() = 0;
virtual void setHdd() = 0;
ComputerSet* returnComputerSet() { return cs; }
};
Il metodo setHdd()
del generatore di calcestruzzo non richiede parametri
class AmdelBuilder : public Builder {
public:
void createComputerSet();
void setHdd();
AmdelBuilder(int capacity = 500) : Builder(capacity) { }
};
void AmdelBuilder::createComputerSet() {
cs = new ComputerSet("Amdel");
}
void AmdelBuilder::setHdd() {
cs->setHdd("Orange", hddCapacity);
}
Il regista non conosce il parametro hddCapacity
class Director {
private:
Builder *builder;
public:
void setBuilder(Builder *b) { builder = b; }
void constructComputerSet();
ComputerSet* getComputerSet() { return builder->returnComputerSet(); }
};
void Director::constructComputerSet() {
builder->createComputerSet();
builder->setHdd();
}
perché il client imposta AmdelBuilder.hddCapacity
quando il client crea l'istanza Builder
Director* d = new Director();
Builder* ba = new AmdelBuilder(750); // Amdel + Orange HDD 750
d->setBuilder(ba);
d->constructComputerSet();
ComputerSet* csa750 = d->getComputerSet();
Altra soluzione
Il generatore non memorizza hddCapacity
, ma il metodo setHdd()
richiede il parametro hddCapacity
.
class Builder {
protected:
ComputerSet *cs;
public:
Builder() { }
virtual void createComputerSet() = 0;
virtual void setHdd(int hddCapacity) = 0;
ComputerSet* returnComputerSet() { return cs; }
};
Il director è l'utente diretto dell'API del builder. Quindi il regista deve conoscere il valore hddCapacity
.
Domanda
Quale dovrebbe essere la migliore pratica che non viola i principi OO e l'idea di incapsulamento del modello di builder?