【问题标题】:Should a Builder be created by a Factory?建造者应该由工厂创建吗?
【发布时间】:2013-06-14 10:02:44
【问题描述】:

复杂对象通常由 Builder 模式创建,其中 Builder 是专门用于通过多种方法创建单个对象的对象。例如。 (伪代码):

class PersonBuilder
    PersonBuilder Named(string name)
        this.name = name
        return this

    PersonBuilder Aged(int age)
        this.age = age
        return this

    Person GetPerson()
        return new Person(this.name, this.age)

这种具有流畅接口的有状态结构在构建器模式中很常见:

myPerson = builder.Named("John").Aged(20).GetPerson();

因此,创建一个可以从中创建构建器的PersonBuilderFactory 是否明智? 或者您应该只依赖PersonBuilder 并假设您的类将被注入一个新对象?后一种方法意味着当您创建 Person 后,您可能应该重置构建器。这里的最佳做法是什么,为什么?

【问题讨论】:

  • 使您的构建器不可变,无需“重置”它们。
  • @BenJames 这可能是我见过的最好的答案..!如果您可以将其写为答案(并可能举例说明如何执行此操作),我很乐意接受它

标签: oop design-patterns


【解决方案1】:

在 C# 中,我喜欢以下模式(需要 lambda 函数):

var person = PersonBuilder.Create(p => p.Named("John").Aged(20));

它在哪里实现了这样的东西:

class PersonBuilder
{
    private PersonBuilder() { /* ... */ }

    public static Person Create(Action<PersonBuilder> configure)
    {
        var builder = new PersonBuilder();
        configure(builder);
        return builder.Build();
    }

    // ...
}

【讨论】:

  • 这是很好的语法,但引入了 PersonBuilder 的具体依赖。出于这个原因,我从不使用静态工厂方法。
【解决方案2】:

在原来的Builder pattern中有一个导演。
据我了解,导演将工作与建设者封装在一起。 如果您需要以某种方式重置构建器(例如,在构建器中为 TcpClient 调用 dispose),请在 director 中执行此操作。

如何以及在何处实例化具体构建器?
例如,如果您有 3 种文档格式(TXT、PDF、Excel)并且用户可以从中选择,则在这种情况下您将需要工厂

class BuilderFactory
{
  public IBuilder Create(DocumentType type)
  {
    if(type == DocumentType.TXT)
      return new TxtBuilder();

    if(type == DocumentType.Pdf)
      return new PdfBuilder();

    //...
  }
}

如果在应用程序执行期间构建器是相同的,您可以将具体构建器放在配置中(尤其是如果您使用 DI)。
如果您在设计时就知道具体的构建器,则可以使用 new 运算符。

【讨论】:

  • 如果你使用new 操作符,那么你就违反了依赖倒置原则。你永远不应该“知道”具体的构建器类型 - 程序到合同
  • director 不应该知道具体的builder,但是使用director 的代码可以使用new 操作符。它显然违反了依赖倒置原则,但在某些情况下对我来说没问题。考虑到你不在你的项目中使用依赖注入并且你严格遵循 D 原则——你将拥有 IDirector、IBuilder 和一个或几个工厂接口,它们的实现,我猜单例包含工厂的具体实例......嗯,所有这些而不仅仅是 IBuilder、Director 和 ConcreteBuilder
【解决方案3】:

这在很大程度上取决于您的构建器类...当我必须在 Builder 创建中应用一些逻辑时,我更喜欢创建一个 XXXBuilderFactory,否则,我认为,只需构建器就足够了,这样的代码保持简单。

【讨论】:

    【解决方案4】:

    (由于我的问题的评论者没有选择回答我将提供一个我认为可以接受的答案)

    使您的 Builder 不可变是解决此问题的方法。您可以利用一个实例可以访问另一个实例的私有成员这一事实。原来的例子就变成了:

    class PersonBuilder
        PersonBuilder(PersonBuilder copy) // copy constructor
            this.name = copy.name
            this.age = copy.age
    
        PersonBuilder Named(string name)
            newBuilder = new PersonBuilder(this)
            newBuilder.name = name
            return newBuilder
    
        PersonBuilder Aged(int age)
            newBuilder = new PersonBuilder(this)
            newBuilder.age = age
            return newBuilder
    
        Person GetPerson()
            return new Person(this.name, this.age)
    

    这样,仍然可以像这样使用构建器:

    myPerson = builder.Named("John").Aged(20).GetPerson();
    

    但是,原来的builder对象会保持不变,可以重复使用。

    感谢 Ben James 的建议。

    【讨论】:

      【解决方案5】:

      通常 builder 应该是不可变的,其中不应该有重置的概念。但这仍然取决于您需要实现的目标....

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-11-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多