Design Patterns - Een korte handleiding voor het Builder-patroon.

Het Builder-patroon is een creatief ontwerppatroon dat de constructie van complexe objecten stap voor stap (of steen voor steen) afhandelt. Het is waarschijnlijk het gemakkelijkste patroon om te zien of de bestaande code een dergelijk ontwerp nodig heeft. Met het Builder-patroon kunnen verschillende weergaven van een object worden gemaakt met dezelfde constructiecode.

Bouwerspatroon is geclassificeerd in de creatieve ontwerppatronen die allemaal over klasse / object-instantiatie gaan. Meer precies, hoe overerving (klassecreatiepatronen) of delegatie (objectcreatiepatronen) effectief te gebruiken. [door Design Patterns eenvoudig uitgelegd]

Voorbeeld: laten we beginnen met een voorbeeld over mijn favoriete onderwerp. Voedsel!! Meer precies zal het voorbeeld gaan over pizza. Pizza wordt gescheiden in drie lagen, het deeg, de saus en de toppings. Laten we ons nu afvragen, hoe zouden we een pizza-object 'construeren' dat uit verschillende toppings en een verscheidenheid aan sauzen kiest.

Vragen voordat u een oplossing voor dit probleem ontwerpt:

  • Hoe zou de bouwer van de Pizza-klasse eruit zien?
  • Moeten we meerdere overbelastende constructeurs hebben met parameters voor alle combinaties zoals sauzen, toppings plus sauzen, enz? Wat als we ook verschillende soorten deeg hebben?
  • Wat als we later besluiten om kaas of verschillende soorten kaas toe te voegen? Zou dat gemakkelijk te implementeren zijn? Hoe zit het met afhankelijkheden van bestaande code?
  • Moeten we gewoon een standaardconstructor hebben die een 'gewone pizza' maakt? Zou dat de klant niet dwingen een aantal setters dienovereenkomstig te bellen? Is dit veilig Wat als de klant vergeet en dan sturen we een gewone pizza naar de klant?

De oplossing voor al deze vragen is het bouwerspatroon. We hebben een 'bouwer' nodig om stap voor stap een pizza te bouwen. Tegelijkertijd kan de klant een specifieke pizza 'bestellen' en de bouwer deze voor hem laten bouwen.

Stap 1 - Zoekwoorden

  1. Representatie: een object kan verschillende representaties hebben. Een pizza kan bijvoorbeeld tomatenmozzarella-toppings hebben en een andere weergave van pizza zou zijn met champignons en parmaham.
  2. Constructiecode: objecten kunnen op verschillende manieren worden gebouwd, bijvoorbeeld met constructors. Constructors kunnen overbelast zijn, ze hebben dezelfde naam (naam van de klasse) maar een ander aantal / soorten argumenten. Afhankelijk van het aantal en type doorgegeven argumenten, wordt de specifieke constructor aangeroepen.

Het is gemakkelijk om in de val te lopen met een klasse van talloze constructeurs waarbij elke een ander aantal parameters neemt. De ontwikkelaar wordt gedwongen om de klasse te instantiëren met de juiste combinatie van parameters voor elke situatie. Dit probleem heeft een naam, het is een populair antipatroon dat de telescopische constructor wordt genoemd en het bouwerpatroon is hiervoor de oplossing. Laten we het Builder-patroon te simpel maken door ronduit te zeggen:

Het hoofddoel van het Builder-patroon is om het minimum aantal overbelastende constructors te hebben ter ondersteuning van de constructie van verschillende representaties van een object.

Stap 2 - Diagram (volgens voorbeeld)

Laten we bij het pizzavoorbeeld blijven. Samenvattend hebben we de pizzacursus, de kok en de betonbouwers die erven van de abstracte bouwer. We zullen het diagram van onder naar boven toelichten:

  • Pizza_Product: deze klasse is de werkelijke pizza. Het wordt weergegeven door drie attributen: (1) Deeg, (2) saus en (3) toppings.
  • ConcreteBuilder: Elke betonbouwer is verantwoordelijk voor een specifieke weergave. In dit voorbeeld hebben we Margherita en Spicy Pizza. Beide betonbouwers zijn verantwoordelijk voor het "bouwen" van hun eigen vertegenwoordiging van pizza op basis van de drie hierboven genoemde kenmerken.
  • AbstractBuilder: bevat een variabele voor een pizzaelid en de betonbouwers erven ervan.
  • Cook_Director: In dit voorbeeld is de regisseur de werkelijke kok. De klas is verantwoordelijk voor het initiëren van de constructie van een gegeven voorstelling door de stukken zo samen te stellen dat de bouwer de aanwijzingen van de regisseur volgt en zich aanpast.

Stap 3 - Codeer als voorbeeld

Ik zou willen voorstellen om de code klasse per klasse te kopiëren uit mijn git-repository “Andreas Poyias” of de onderstaande fragmenten (in de aangegeven volgorde) en deze in een van de beschikbare online C ++ editors te plakken zoals c ++ shell, jdoodle, onlineGDB en voer het uit om de output te observeren. Lees dan de opmerkingen of beschrijving hieronder. Neem de tijd om het grondig te lezen (dat betekent één minuut, niet minder en niet meer).

Artikel:
Dit is de pizzacursus. Het is een eenvoudige klasse met drie setters en een taste () -methode die alle ingrediënten afdrukt.

# include 
#include  // unique_ptr
namespace std; gebruiken;
klasse Pizza_Product
{
openbaar:
 void setDough (const string & dough) {m_dough = dough; }
 void setSauce (const string & sauce) {m_sauce = sauce; }
 void setTopping (const string & topping) {m_topping = topping; }
void taste () const
{
  cout << "Pizza met" << m_dough << "deeg,"
       << m_sauce << "saus en"
       << m_topping << "topping. Mmmmmmm." << endl;
}
privaat:
 string m_dough;
 string m_sauce;
 string m_topping;
};

Abstracte bouwer:
De abstracte builder is een interface die een pizza-object bevat. Het heeft een getter die het pizza-object retourneert en een methode om het pizza-object te instantiëren. Het verklaart ook de drie bouwermethoden die de betonbouwers verderop zullen implementeren.

klasse Pizza_Builder
{
openbaar:
  virtual ~ Pizza_Builder () {};
  Pizza_Product * getPizza () {return m_pizza.release (); }
  void createNewPizzaProduct ()
  {
    m_pizza = make_unique  ();
  }
  virtuele leegte buildDough () = 0;
  virtuele leegte buildSauce () = 0;
  virtuele leegte buildTop () = 0;
beschermd:
  unique_ptr  m_pizza;
};

Betonbouwers:
Twee voorbeelden van betonbouwers en twee voorstellingen van een pizza. Beiden implementeren hun eigen build-methoden met behulp van het object m_pizza uit de bovenliggende klasse (Abstract builder)

class Margherita_ConcreteBuilder: public Pizza_Builder
{
openbaar:
 virtual void buildDough () {m_pizza-> setDough ("cross"); }
 virtual void buildSauce () {m_pizza-> setSauce ("tomaat"); }
 virtual void buildTop () {m_pizza-> setTopping ("mozzarela + basilicum"); }
};
class Spicy_ConcreteBuilder: openbare Pizza_Builder
{
openbaar:
 virtual void buildDough () {m_pizza-> setDough ("pan gebakken"); }
 virtual void buildSauce () {m_pizza-> setSauce ("tomaat + chili"); }
 virtual void buildTop () {m_pizza-> setTopping ("pepperoni + salami"); }
};

Regisseur:
Deze klas brengt alles samen. Het heeft een variabele Pizza_Builder. Het gebruikt makePizza () om een ​​betonbouwer als parameter te ontvangen. Roept vervolgens de buildbewerkingen op die voor beide representaties dienovereenkomstig werken. Daarom bereiken we het doel om één constructiecode te hebben die verschillende soorten pizza's vertegenwoordigt. De methode tastePizza () is om de inhoud van een pizza af te drukken.

klasse Cook_Director
{
openbaar:
 void tastePizza () {m_pizzaBuilder-> getPizza () -> taste (); }
void makePizza (Pizza_Builder * pb)
 {
   m_pizzaBuilder = pb;
   m_pizzaBuilder-> createNewPizzaProduct ();
   m_pizzaBuilder-> buildDough ();
   m_pizzaBuilder-> buildSauce ();
   m_pizzaBuilder-> buildTop ();
 }
privaat:
 Pizza_Builder * m_pizzaBuilder;
};

Hoofd (klant):
 De hoofdmethode werkt als de client (hetzelfde als de vorige handleidingen). Het is zo gemakkelijk voor de klant om verschillende weergaven van een pizza te kunnen maken. We hebben de regisseur nodig, en dan, door gewoon twee verschillende betonbouwers door te geven als parameter voor de pizza, kunnen we twee verschillende soorten pizza's proeven.

int main ()
{
  Cook_Director kok;
  Margherita_ConcreteBuilder margheritaBuilder;
  Spicy_ConcreteBuilder spicyPizzaBuilder;
  cook.makePizza (& margheritaBuilder);
  cook.tastePizza ();
  cook.makePizza (& spicyPizzaBuilder);
  cook.tastePizza ();
}
// Uitgang
// Pizza met kruisdeeg, tomatensaus en mozzarela + basilicum topping. // Mmmmmmm.
// Pizza met gebakken deeg, tomaat + chilisaus en
// pepperoni + salami topping. Mmmmmmm.

Laten we de voordelen van dit patroon samenvatten:

  • Er zijn veel mogelijke weergaven, maar slechts één gemeenschappelijke invoer.
  • We hebben een standaardprotocol voor het maken van alle mogelijke representaties. De stappen van dit protocol worden duidelijk zichtbaar gemaakt door een Builder-interface.
  • Er is één afgeleide concrete klasse voor elke doelrepresentatie.
  • Het is gemakkelijk voor een ontwikkelaar om een ​​nieuwe, zelfonderhoudende en onafhankelijke weergave (van een pizza) toe te voegen zonder de angst iets anders te breken.
  • De klant kan de ConcreteBuilder eenvoudig registreren bij de directeur en de verwachte weergave krijgen.

De volgende blog is een beknopte handleiding voor het ontwerppatroon van Decorator. Het is een structureel patroon dat een must is voor uw kennisrepository. Vergeet niet mijn blogpost leuk te vinden / te klappen en mijn account te volgen. Dit is om mij de voldoening te geven dat ik sommige mede-ontwikkelaars heb geholpen en me aanspoort om te blijven schrijven. Als er een specifiek ontwerppatroon is waarover je meer wilt weten, laat het me dan weten in de reacties hieronder, zodat ik het je de komende weken kan aanbieden.

Andere snelgidsen over ontwerppatronen:

  1. Design Patterns - Een korte handleiding voor Abstract Factory.
  2. Design Patterns - Een korte handleiding voor Bridge Pattern.
  3. Design Patterns - Een korte handleiding voor Builder Pattern.
  4. Design Patterns - Een korte handleiding voor Decorator Pattern.
  5. Ontwerppatronen - Een korte handleiding voor gevelpatroon.
  6. Ontwerppatronen - Een korte handleiding voor het waarnemerspatroon.
  7. Design Patterns - Een korte handleiding voor Singleton Pattern.