Builder设计模式:为什么我们需要Director?

Ari*_*Ari 34 design-patterns builder

最近我遇到了Builder设计模式.似乎不同的作者使用"Builder模式"来引用不同的风格,所以让我描述一下我所询问的模式.

我们有一个用于创建产品的算法,即不同类型的对象.在足够高的抽象级别,所有产品类型的算​​法都是相同的,但每种产品类型都需要对每个算法的抽象步骤进行不同的实现.例如,我们可能有以下蛋糕烘焙算法:

 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.
Run Code Online (Sandbox Code Playgroud)

不同的蛋糕将需要这些步骤的不同实施,即,使用什么液体/干燥成分,混合的速度,烘烤多长时间等.

模式说是这样做的.对于每个产品,我们创建一个具体的构建器类,其中包含上述每个步骤的实现.所有这些类都派生自一个抽象构建器基类,它本质上是一个接口.因此,例如,我们将有一个抽象基类CakeBaker与纯虚方法AddLiquid(),MixLiquids()等等.具体的蛋糕面包师将是具体的子类,例如,

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
   }
      ...
      ...
};
Run Code Online (Sandbox Code Playgroud)

LemonCitrusCakeBaker也将是其子类CakeBaker,但在其方法中将使用不同的成分和数量.

不同的蛋糕类型将类似地是抽象Cake基类的子类.

最后,我们有一个类来实现抽象算法.这是导演.在面包店的例子中我们可以称之为ExecutiveBaker.此类将接受(来自客户端)具体的构建器对象并使用其方法来创建和返回所需的产品.

这是我的问题.为什么我们需要导演与抽象构建器分开?为什么不将它们转换为单个构建器抽象基类,使原始抽象构建器的公共方法受到保护(具体子类将像以前一样覆盖它们).

izi*_*tti 22

Builder模式的核心部分涉及Abstract Builder及其子类(具体构建器).根据GoF的设计模式,导演只是"在构建产品的一部分时通知建筑商",这可以由客户完美地完成.

Java API中的StringBuilder类是没有相应控制器的构建器的示例 - 通常客户端类"指示"它.

此外,在有效Java创建和销毁Java对象中,Joshua Bloch建议使用构建器模式,并且他不包括导演.


dra*_*nli 12

Builder模式的GoF变体没有Builder WITHOUT Director.对此有不同的看法,但我会进一步解释.

Builder模式的目的是为您提供多种方法来创建相同的对象.Builder应该只有构建对象不同部分的方法,但是algorythm--这些函数的执行方式 - 应该是Director的关注点.如果没有董事,每个客户都需要完全了解建筑的工作原理.但是使用Director,客户需要知道的是在特定情况下使用的Builder.

那么,我们这里有两个部分:

  1. 构建器,逐个创建对象的一部分.需要注意的重要一点是,为此,他保持创建对象的状态.
  2. Director,用于控制构建器的执行方式.

现在到了我之前提到的那一点.模式的Builder部分在其他情况下很有用,并且不同的供应商使用它而不是Director用于不同的目的.这种用法的具体示例是Doctrine Query Builder.

这种方法的缺点是,当Builder开始构建一个对象时,他变为有状态,如果客户端在创建对象后没有重置Builder,则另一个客户端或多次使用的同一个客户端可以获得这些部件之前创建的对象.因此,Doctrine使用Factory模式来创建Builder的每个实例.

我希望这有助于谷歌搜索.


Mic*_*ann 9

如果您分别进入Director和Builder,您已经记录了从一组零件(主管)组装产品的不同责任以及创建零件(构建器)的责任.

  • 在构建器中,您可以更改零件的构建方式.在你的情况下,是否AddLiquid()应该添加奶油或牛奶.
  • 在导演中,您可以更改如何组装零件.在你的情况下,通过使用AddChocolate()而不是AddFruits()你得到一个不同的蛋糕.

如果你想要这种额外的灵活性,我会重命名为(因为在构建器中使用baker建议,这是组装零件的构建者工作)

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();
     }
}
Run Code Online (Sandbox Code Playgroud)

  • 但这种分离有什么好处呢?如果我想要不同的组装协议,我需要有不同的控制器,每个控制器都针对特定的(抽象)构建器量身定制。这些控制器不共享任何可以抽象的通用功能,除了 MakeSomething(),它没有用。您始终可以将任何任务分解为更小的子任务和职责,并且始终可以向任何模式添加另一个级别的“经理”或“主管”。我认为这种模式的作者可能正在按照您概述的思路进行思考,但我看不到其中的价值。 (2认同)