web-dev-qa-db-ja.com

ビルダーのデザインパターン:なぜディレクターが必要なのですか?

最近、Builderのデザインパターンに出くわしました。作者によって「ビルダーパターン」を使ってフレーバーが違うようですので、質問しているパターンについて説明します。

products、つまり異なるタイプのオブジェクトを作成するためのアルゴリズムがあります。十分に高いレベルの抽象化では、アルゴリズムはすべての製品タイプで同じですが、製品タイプごとに、アルゴリズムの抽象化ステップごとに異なる実装が必要です。たとえば、次のケーキ焼きアルゴリズムがあるとします。

_ 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.
_

ケーキが異なれば、これらのステップの実装も異なります。つまり、使用する液体/乾燥材料、混合速度、焼き時間などです。

パターンはそうするように言っています。製品ごとに、上記の各ステップの実装を含むconcrete builderクラスを作成します。これらのクラスはすべて、本質的にインターフェイスであるabstract builder基本クラスから派生しています。したがって、たとえば、純粋仮想メソッドAddLiquid()MixLiquids()などを持つ抽象基本クラスCakeBakerがあります。具体的なケーキベイカーは、具体的なサブクラスになります。 、

_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
   }
      ...
      ...
};
_

LemonCitrusCakeBakerCakeBakerのサブクラスになりますが、メソッドで異なる成分と量を使用します。

異なるケーキタイプも同様に、抽象Cake基本クラスのサブクラスになります。

最後に、抽象アルゴリズムを実装するためのクラスがあります。これはディレクターです。パン屋の例では、それをExecutiveBakerと呼ぶことがあります。このクラスは、(クライアントから)具体的なビルダーオブジェクトを受け入れ、そのメソッドを使用して、目的の製品を作成して返します。

これが私の質問です。なぜディレクターを抽象ビルダーから分離する必要があるのですか?それらを単一のビルダー抽象基本クラスにロールして、元の抽象ビルダーのパブリックメソッドを保護します(そして、具象サブクラスは以前のようにこれらをオーバーライドします)。

36
Ari

Builderパターンのコア部分は、Abstract Builderとそのサブクラス(コンクリートビルダー)に関係します。 GoFのデザインパターン によると、directorは単に「製品の一部をビルドする必要があるときはいつでもビルダーに通知する」だけであり、これはクライアントが完全に行うことができます。

Java APIの StringBuilder クラスは、それぞれのダイレクタがないビルダーの例です。通常、クライアントクラスがそれを「指示」します。

また、 Effective Java および Creating and Destroying Java Objects )で、Joshua Blochはビルダーパターンの使用を提案しており、彼は含まれていません。ディレクター。

23
izilotti

BuilderパターンのGoFバリエーションには、DirectorなしのBuilderはありません。これには別のポイントがありますが、さらに説明します。

Builderパターンのポイントは、同じオブジェクトを作成するための複数の方法を提供することです。 Builderには、オブジェクトのさまざまな部分を構築するメソッドのみが必要ですが、アルゴリズム(これらの関数の実行方法)は、Directorの関心事である必要があります。ディレクターがいなければ、すべてのクライアントは建物がどのように機能するかを正確に知る必要があります。しかし、Directorを使用すると、クライアントが知る必要があるのは、特定の場合にどのBuilderを使用するかだけです。

したがって、ここにあるのは2つの部分です。

  1. オブジェクトの一部を1つずつ作成するビルダー。注意すべき重要なことは、このために作成されたオブジェクトの状態を保持することです。
  2. Builderの機能の実行方法を制御するDirector。

さて、先ほど言及したところまで。パターンのBuilder部分は他の場合に役立ち、Directorなしでさまざまなベンダーによってさまざまな目的で使用されています。このような使用の具体例は、 Doctrine Query Builder です。

このようなアプローチの欠点は、ビルダーがオブジェクトの構築を開始するとステートフルになり、オブジェクトの作成後にクライアントがビルダーをリセットしない場合、別のクライアントまたは複数回使用された同じクライアントがパーツを取得する可能性があることです。以前に作成されたオブジェクトの。このため、Doctrineはファクトリパターンを使用して、Builderのすべてのインスタンスを作成します。

これがグーグルに役立つことを願っています。

15
drakonli

DirectorとBuilderに分かれている場合は、一連のパーツから製品を組み立てる責任(director)とパーツを作成する責任(builder)の異なる責任を文書化しています。

  • ビルダーでは、パーツのビルド方法を変更できます。あなたの場合、AddLiquid()がクリームまたは牛乳を追加する必要があるかどうか。
  • ディレクターでは、パーツの組み立て方法を変更できます。あなたの場合、AddChocolate()の代わりにAddFruits()を使用すると、別のケーキが得られます。

この追加の柔軟性が必要な場合は、名前を変更します(ビルダーでベイカーを使用することが示唆されているため、パーツを組み立てるのはビルダーの仕事でした)

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();
     }
}
10

乾燥した材料を使わずにケーキを作りたいとしましょう。これから行うことは、Directorに新しいメソッドを追加するか、別のDirectorを作成することです。これにより、継承の複雑さからユーザーを保護し、コードをより柔軟にすることができます。

2

仰るとおりです。もう1つのアプローチは、CakeBakerに、アルゴリズムが実行されるCake(Cakeクラス)メソッドとMakeCake()メソッドを返すGetCake()メソッドが必要だと思います。それは問題ありませんが、一方で責任ある分離があります。抽象ビルダーと特定のバルダーをケーキパーツのみのビルダーと見なし、ディレクターをマネージャーまたはデザイナーと見なします。その責任はケーキの組み立てと製造です。

0
Arseny

Builderは、特定の手順を実行する方法を知っています。ディレクターは、ビルダーの手順を使用してすべてを組み立てる方法を知っています。

彼らは一緒に働きます。

このパターンで私が見ることができる唯一の脆弱性は、クライアントがDirectorなしでBuilderメソッドを直接呼び出すことができることです-これはいくつかの問題と一貫性の欠如をもたらす可能性があります(たとえば、アルゴリズム全体の一部であるInitメソッドを呼び出さない)

0