3

我最近一直在与一个研究小组一起研究设计模式,并且已经了解到构建器模式对于创建由许多(可能是可选的)部分组成的复杂对象非常有用。

但是,是否存在建造商做得太多的地方?假设我们有一个包含许多不同对象组合的类,是否有另一种模式可能更适合于此,而不是制作数十个不同的构建器?是否可以通过不制作完全特定的构建器来减少所需的构建器数量?

我和我的学习小组经常提到的例子是汽车制造商,例如在汽车公司的网站上。任何一家汽车公司都有数十辆汽车,每辆都有许多不同的功能、颜色、附加功能等。按照我的理解,您的构建器应该特定于您正在制作的确切对象,因此将构建器模式应用于此示例将产生数百个看起来像“RedSUVWithSunroofBuilder”、“BlueSUVWithSunroofBuilder”、“RedSUVBuilder”等的建造者。

是否有任何理由使用构建器模式,我无法传递其中一些值来减少我需要创建的构建器的数量?例如,不是使用 RedSUVWithSunroofBuilder 或 BlueSUVWithSunroofBuilder,而是使用 SUVWithSunroofBuilder("Red") 和 SUVWithSunroofBuilder("Blue") 是否仍然适合构建器模式,还是更适合不同的模式?

4

5 回答 5

5

好吧,几个月前我想知道同样的问题,我想出了一个新的设计模式,叫做step builder pattern这里有这个模式的完整描述)。

在这个设计中,我提供了设计路径的可能性,避免了创建多个构建器的需要。

这个概念很简单:

  1. 在内部类或接口中编写创建步骤,其中每个方法都知道接下来可以显示什么。
  2. 在内部静态类中实现所有步骤接口。
  3. 最后一步是 BuildStep,负责创建您需要构建的对象。
于 2012-11-25T11:38:03.087 回答
5

构建器模式当然是自由裁量的,如果它过于复杂那就过于复杂了,你可能需要考虑一种不同的方式来创建对象,比如工厂模式。我认为构建器模式在以下几种情况下表现出色:

  • 创建具有多个有效配置的对象
    • 我认为你的汽车就是一个很好的例子
  • 创建不可变对象,其中并非所有必需的数据都可以预先提供
    • 有关这个的一个很好的例子,请查看番石榴不可变集合构建器,例如ImmutableSet.Builder

以下是如何实现汽车制造商的一个示例:

public class Car {
    private final boolean hasSunroof;
    private final Color color;
    private final int horsePower;
    private final String modelName;

    private Car(Color color, int horsePower, String modelName, boolean hasSunroof) {
        this.color = color;
        this.horsePower = horsePower;
        this.hasSunroof = hasSunroof;
        this.modelName = modelName;
    }

    public static Builder builder(Color color, int horsePower) {
        return new Builder(color, horsePower);
    }

    public static class Builder {
        private final Color color;
        private final int horsePower;
        private boolean hasSunroof;
        private String modelName = "unknown";

        public Builder(Color color, int horsePower) {
            this.color = color;
            this.horsePower = horsePower;
        }

        public Builder withSunroof() {
            hasSunroof = true;
            return this;
        }

        public Builder modelName(String modelName) {
            this.modelName = modelName;
            return this;
        }

        public Car createCar() {
            return new Car(color, horsePower, modelName, hasSunroof);
        }
    }
}

Builder 不一定是嵌套类,但它确实允许您向可能滥用您的 API 的人隐藏您的构造函数。另请注意,必须提供最低限度的必需参数才能创建构建器。你可以像这样使用这个构建器:

Car car = Car.builder(Color.WHITE, 500).withSunroof().modelName("Mustang").createCar();
于 2011-01-19T17:04:45.340 回答
2

您通常会看到的 Builder 模式(尤其是在 Java 中)是给定对象的单个 Builder,它具有用于设置不同参数的流畅界面。这是有效 Java 中提倡的模式,这就是为什么您通常会在 Java 中看到这种模式。

如果你有一些非常不同的构建场景,当然,不同的构建器对象可能是有意义的,但我会退后一步。如果你在一个对象中有这么多不同的属性,你可能有一个对象做的太多了,问题的一个症状是构建器的复杂性。

当然,现实世界的对象是复杂的,但面向对象设计的基本论点是,如果复杂性在抽象中分层,那么在任何给定的层,您一次处理 5 到 7 个属性,您可以限制问题变成太复杂而无法在给定层理解。

正确设计抽象层的困难使得上述理想主义与现实一样多,但我认为让 Object 做太多事情的意义在于。

在您的汽车示例中,汽车有很多层,但保持可管理性的诀窍是区分不同的组件,并让较大的汽车由几个装配选项构建而成,这些装配选项本身由几个装配选项构建,等等。每个程序集都值得拥有自己的对象,并可能拥有自己的构建者。

于 2011-01-19T17:02:47.690 回答
2

在我看来,您使用了太多的构建器,而每个构建器都做得不够。每个类都应该有一个构建器。您似乎正在尝试为每组字段值创建一个新的构建器。相反,每个字段都应该在单个构建器中具有一个或两个方法。您的构建器调用最终应该看起来像这样

Car c = new SUVBuilder().withSunroof().withColor("Red").build();

以上假设SUV是汽车的子类。如果不是,那么您可以在附加功能中指定 SUV。

于 2011-01-19T17:04:08.733 回答
0

构建器允许您将一系列复杂的选择封装到一个简单的算法中。例如,如果您的组装流水线正在制造汽车,它可能:

buildChassis();
buildDriveTrain();
buildBody();
assemble();
paint();

您可能需要为不同的底盘、传动系统和车身使用单独的混凝土制造商,但您可能只需要一个用于油漆的项目——您只需在 c'tor 中将其参数化为颜色即可。您的主管类知道您的构建器的详细信息是什么,因此它会实例化正确的具体构建器;您的装配线客户只是按顺序运行这些步骤。您可以将具体构建器参数化以获取任何非基本细节(基本的是判断调用)。

于 2011-01-19T17:00:19.063 回答