Builder 模式:Director 的目的是什么?

bub*_*ing 4 c# design-patterns builder

首先,我想承认这个问题与另一个问题非常相似,但我想问更具体的问题,并希望获得更高质量的答案。

最近,我学习了一个教程,其中使用 Director 实现了 Builder 模式。为了演示目的,我简化了课程:

public class Director
{
    private readonly Builder _builder;

    public Director(Builder builder)
    {
        _builder = builder;
    }

    public void BuildProduct()
    {
        _builder.CreateProduct();
        _builder.BuildPart1();
        _builder.BuildPart2();
    }

    public Product GetProduct() => _builder.GetProduct();
}

public abstract class Builder
{
    protected Product Product;

    internal void CreateProduct()
    {
        Product = new Product();
    }

    internal Product GetProduct() => Product;

    internal abstract void BuildPart1();

    internal abstract void BuildPart2();
}

public class Thing1Builder : Builder
{
    internal override void BuildPart1() => Product.ThingStrings.Add("Thing-1 String-1");

    internal override void BuildPart2() => Product.ThingStrings.Add("Thing-1 String-2");
}

public class Thing2Builder : Builder
{
    internal override void BuildPart1() => Product.ThingStrings.Add("Thing-2 String-1");

    internal override void BuildPart2() => Product.ThingStrings.Add("Thing-2 String-2");
}

public class Product
{
    internal readonly ICollection<string> ThingStrings = new List<string>();

    public void Display()
    {
        foreach (string thingString in ThingStrings)
        {
            Console.WriteLine($"Thing string = {thingString}");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当我按照教程进行操作时,我忍不住想知道为什么我们不将 Director 唯一有意义的方法(BuildProduct 方法)放入构建器的抽象基类中。这仍然确保所有混凝土建筑商获得相同的建筑模板,并消除看似无用的层。导演有什么优势?

在这里,我编写了几乎相同的代码,只是没有导演(产品类被省略,因为它没有改变):

public abstract class BuilderWithoutDirector
{
    protected Product Product;

    public void CreateProduct()
    {
        Product = new Product();
        BuildPart1();
        BuildPart2();
    }

    public Product GetProduct() => Product;

    protected abstract void BuildPart1();

    protected abstract void BuildPart2();
}

public class Thing1BuilderWithoutDirector : BuilderWithoutDirector
{
    protected override void BuildPart1() => Product.ThingStrings.Add("Thing-1 String-1");

    protected override void BuildPart2() => Product.ThingStrings.Add("Thing-1 String-2");
}

public class Thing2BuilderWithoutDirector : BuilderWithoutDirector
{
    protected override void BuildPart1() => Product.ThingStrings.Add("Thing-2 String-1");

    protected override void BuildPart2() => Product.ThingStrings.Add("Thing-2 String-2");
}
Run Code Online (Sandbox Code Playgroud)

这两个例子的用法如下:

    private static void UseWithDirector()
    {
        var director = new Director(new Thing1Builder());
        director.BuildProduct();
        var thing1 = director.GetProduct();

        director = new Director(new Thing2Builder());
        director.BuildProduct();
        var thing2 = director.GetProduct();

        thing1.Display();
        thing2.Display();
    }

    private static void UseWithoutDirector()
    {
        var builder1 = new Thing1BuilderWithoutDirector();
        builder1.CreateProduct();
        var thing1 = builder1.GetProduct();

        var builder2 = new Thing2BuilderWithoutDirector();
        builder2.CreateProduct();
        var thing2 = builder2.GetProduct();

        thing1.Display();
        thing2.Display();
    }
Run Code Online (Sandbox Code Playgroud)

这两个方法输出相同的内容。我看到了Director版本的一个优点,即您创建一个director并与多个构建器重用它,这有一种知道正在发生什么的顶级对象的感觉(请原谅那里的模糊逻辑),但您仍然必须了解并创建两个不同的构建器,那么为什么不直接使用它们呢?

Ngh*_*Bui 5

PuttingDirector的工作Builder违反了单一责任原则,因为遗嘱Builder有两个责任:

  1. Builder 的职责:知道如何实现 BuildPart1BuildPart2方法。
  2. 董事的责任:知道哪些部分应该按什么顺序使用。

事实上,例如,当您更改基类BuildPart1和中的调用顺序时,所有具体的都会受到不必要的影响(它们必须重新编译和重新部署)。BuildPart2BuilderThing*Builder

  • 我发现 SRP 的解释很有说服力。有时,当这些问题如此抽象并且参考材料中没有提供更接近现实世界的解决方案时,我有时会遇到这些问题。重新编译/重新部署点是一个我没有考虑的好点,因为在我看来它们是同一个程序集的一部分。在不同的环境中,我可以想象客户制作构建器并将它们连接到我的主管,让我了解构建顺序,而不必在构建顺序更改时强制客户进行更改。简而言之,这是一个很好、明确的答案,有具体的分手原因。 (2认同)