49 Stimmen

Entwurfsmuster des Builders: Warum brauchen wir einen Director?

Kürzlich bin ich auf das Entwurfsmuster Builder gestoßen. Es scheint, dass verschiedene Autoren "Builder-Muster" verwenden, um sich auf verschiedene Varianten zu beziehen, also lassen Sie mich das Muster beschreiben, nach dem ich frage.

Wir haben einen Algorithmus zur Erstellung von Produkte d. h. Objekte unterschiedlicher Art. Auf einer ausreichend hohen Abstraktionsebene ist der Algorithmus für alle Produkttypen gleich, aber jeder Produkttyp erfordert eine andere Implementierung der einzelnen abstrakten Schritte des Algorithmus. Zum Beispiel könnte der folgende Algorithmus zum Kuchenbacken verwendet werden:

 1. Add liquids.
 2. Mix well.
 3. Add dry ingredients.
 4. Mix well.
 5. Pour batter into baking pan.
 6. Bake.
 7. Return baked cake.

Verschiedene Kuchen erfordern unterschiedliche Umsetzungen dieser Schritte, d. h. welche Flüssigkeiten/Trockenzutaten zu verwenden sind, mit welcher Geschwindigkeit zu mischen ist, wie lange zu backen ist, usw.

Das Muster sagt, dass man es so machen soll. Für jedes Produkt erstellen wir eine Betonbauer Klasse mit einer Implementierung für jeden der oben genannten Schritte. Alle diese Klassen sind abgeleitet von einer abstrakter Konstrukteur Basisklasse, die im Wesentlichen eine Schnittstelle ist. Wir haben also zum Beispiel eine abstrakte Basisklasse CakeBaker mit rein virtuellen Methoden AddLiquid() , MixLiquids() , usw. Die konkreten Kuchenbäcker werden konkrete Unterklassen sein, z.B.,

class ChocolateCakeBaker : public CakeBaker {
public:
   virtual void AddLiquids()
   {
        // Add three eggs and 1 cup of cream
   }

   virtual void AddDryIngredients()
   {
       // Add 2 cups flour, 1 cup sugar, 3 tbsp cocoa powder,
       // 2 bars ground chocolate, 2 tsp baking powder
   }
      ...
      ...
};

En LemonCitrusCakeBaker wäre auch eine Unterklasse von CakeBaker Er verwendet jedoch andere Zutaten und Mengen für seine Methoden.

Die verschiedenen Kuchentypen sind ebenfalls Unterklassen eines abstrakten Cake Basisklasse.

Schließlich haben wir eine Klasse zur Implementierung des abstrakten Algorithmus. Dies ist die Direktor . In dem Beispiel der Bäckerei könnte man es als ExecutiveBaker . Diese Klasse würde (vom Kunden) ein konkretes Builder-Objekt akzeptieren und dessen Methoden verwenden, um das gewünschte Produkt zu erstellen und zurückzugeben.

Hier ist meine Frage. Warum muss der Regisseur vom abstrakten Ersteller getrennt sein? Warum nicht rollen sie in einem einzigen Builder abstrakte Basisklasse, so dass die ursprüngliche abstrakte Builder öffentliche Methoden geschützt (und die konkrete Unterklassen überschreiben diese wie zuvor).

34voto

izilotti Punkte 4631

Der Kernteil des Builder-Musters betrifft den abstrakten Builder und seine Unterklassen (konkrete Builder). Gemäß GoFs Entwurfsmuster Der Direktor benachrichtigt den Bauherrn einfach, wenn ein Teil des Produkts gebaut werden soll, was der Kunde problemlos tun kann.

En StringBuilder Klasse in der Java-API ist ein Beispiel für einen Builder ohne den entsprechenden Director - typischerweise wird er von der Client-Klasse "geleitet".

Auch in Leistungsfähiges Java y Erstellen und Zerstören von Java-Objekten Joshua Bloch schlägt die Verwendung des Bauherrenmusters vor, wobei er keinen Regisseur vorsieht.

33voto

drakonli Punkte 2058

In der GoF-Variante des Builder-Musters gibt es den Builder NICHT OHNE den Director. Es gibt noch einen anderen Punkt, den ich weiter erläutern werde.

Der Sinn des Builder-Musters besteht darin, Ihnen mehrere Möglichkeiten zu geben, dasselbe Objekt zu erstellen. Der Builder sollte nur Methoden haben, die verschiedene Teile eines Objekts aufbauen, aber der Algorithmus - die Art und Weise, wie diese Funktionen ausgeführt werden - sollte Sache des Director sein. Ohne den Director hätte jeder Kunde das Bedürfnis, GENAU zu wissen, wie das Gebäude funktioniert. Aber mit dem Director muss der Kunde nur wissen, welchen Builder er in einem bestimmten Fall verwenden muss.

Es handelt sich also um zwei Teile:

  1. Der Builder, der die Teile des Objekts nach und nach erstellt. Wichtig ist, dass er dabei den Zustand des erstellten Objekts beibehält.
  2. Der Direktor, der die Ausführung der Aufgaben des Bauherrn kontrolliert.

Nun zu dem Punkt, auf den ich mich vorhin bezogen habe. Der Builder-Teil des Musters ist auch in anderen Fällen nützlich und wurde von verschiedenen Anbietern OHNE den Director für unterschiedliche Zwecke verwendet. Ein konkretes Beispiel für eine solche Verwendung wäre die Doctrine Query Builder .

Der Nachteil eines solchen Ansatzes ist, dass der Builder, wenn er mit der Erstellung eines Objekts beginnt, zustandsabhängig wird, und wenn der Client den Builder nach der Erstellung des Objekts nicht zurücksetzt, könnte ein anderer Client oder derselbe Client, der mehr als einmal verwendet wurde, die Teile des Objekts erhalten, die zuvor erstellt wurden. Aus diesem Grund verwendet Doctrine das Factory-Muster, um jede Instanz des Builders zu erstellen.

Ich hoffe, das hilft denjenigen, die googeln.

16voto

Micha Wiedenmann Punkte 18706

Durch die Trennung in Director und Builder wird die unterschiedliche Verantwortung für die Zusammenstellung eines Produkts aus einer Reihe von Teilen (Director) und die Verantwortung für die Erstellung des Teils (Builder) dokumentiert.

  • Im Builder können Sie ändern, wie ein Teil aufgebaut ist. In Ihrem Fall, ob ein AddLiquid() sollte Sahne oder Milch hinzufügen.
  • In der Regie können Sie ändern, wie die Teile zusammengesetzt werden sollen. In Ihrem Fall durch Verwendung von AddChocolate() anstelle von AddFruits() erhalten Sie einen anderen Kuchen.

Wenn Sie diese zusätzliche Flexibilität wünschen, würde ich sie umbenennen in (da die Verwendung von baker im Builder andeutet, dass es die Aufgabe des Builders war, die Teile zusammenzubauen)

class LightBakingSteps : public BakingSteps {
public:
    virtual void AddLiquids()
    {
        // Add milk instead of cream
    }

    virtual void AddDryIngredients()
    {
        // Add light sugar
    }

    ...
};

class ChoclateCakeBaker : public CakeBaker {
public:
     Cake Bake(BakingSteps& steps)
     {
         steps.AddLiquieds();
         steps.AddChocolate();      // chocolate instead of fruits
         return builder.getCake();
     }
}

4voto

Aleksander Bethke Punkte 1269

Der Bauherr weiß, wie er bestimmte Schritte ausführen muss. Der Direktor weiß, wie man das Ganze mit Hilfe der Schritte des Erstellers zusammensetzt.

Sie arbeiten zusammen.

Die einzige Schwachstelle, die ich bei diesem Muster sehen kann, ist, dass der Client in der Lage ist, Builder-Methoden direkt ohne Director aufzurufen - das kann einige Probleme und Inkohärenz mit sich bringen (z. B. kein Aufruf der Init-Methode, die Teil des gesamten Algorithmus ist)

3voto

Nehmen wir an, Sie möchten einen Kuchen ohne die trockenen Zutaten backen. Sie fügen einfach eine neue Methode in den Director ein oder erstellen einen weiteren Director. Dies wird Sie vor der Komplexität der Vererbung schützen und Ihren Code flexibler machen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X