Java 构建器模式会做得太多吗?

Java 构建器模式会做得太多吗?,java,design-patterns,class-design,builder,builder-pattern,Java,Design Patterns,Class Design,Builder,Builder Pattern,最近我和一个研究小组一起研究了设计模式,并逐渐了解到构建器模式对于创建由许多(可能是可选的)部件组成的复杂对象非常有用 然而,建设者是否做得太多了?假设我们有一个类,它有许多不同的对象组合,有没有另一种模式更适合这种组合,而不是生成几十个不同的构建器?是否可以通过不制作完全特定的构建器来减少所需的构建器数量 我和我的研究小组经常提到的例子是一家汽车制造商,比如在一家汽车公司的网站上。任何一家汽车公司都有几十辆汽车,每辆都有许多不同的功能、颜色、附加功能等。据我所知,您的构建器应该特定于您正在制作

最近我和一个研究小组一起研究了设计模式,并逐渐了解到构建器模式对于创建由许多(可能是可选的)部件组成的复杂对象非常有用

然而,建设者是否做得太多了?假设我们有一个类,它有许多不同的对象组合,有没有另一种模式更适合这种组合,而不是生成几十个不同的构建器?是否可以通过不制作完全特定的构建器来减少所需的构建器数量

我和我的研究小组经常提到的例子是一家汽车制造商,比如在一家汽车公司的网站上。任何一家汽车公司都有几十辆汽车,每辆都有许多不同的功能、颜色、附加功能等。据我所知,您的构建器应该特定于您正在制作的确切对象,因此将构建器模式应用于此示例将产生数百个类似“RedSuvWithSunRoadBuilder”、“BlueSuvWithSunRoodBuilder”、“RedSUVBuilder”的构建器等等


是否有任何原因,使用构建器模式,我无法传递其中一些值来减少我需要创建的构建器的数量?例如,与其使用RedSuvWithSunRootBuilder或BlueSuvWithSunRootBuilder,不如使用SuvWithSunRootBuilder(“红色”)和SuvWithSunRootBuilder(“蓝色”)是否仍然适合构建器模式,或者这更适合不同的模式?

生成器允许您将一系列复杂的选择封装到一个简单的算法中。例如,如果装配线正在制造汽车,则可能:

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

您可能需要为不同的底盘、传动系和车身使用不同的混凝土构建器,但您可能只需要一个用于喷漆的构建器,您只需在c'tor中将其参数化为颜色即可。director类知道构建器的细节,因此它实例化了正确的具体构建器;您的装配线客户机只是按顺序运行这些步骤。您可以为任何非基本的细节(基本的是判断调用)参数化具体构建器。

您通常会看到的构建器模式(特别是在Java中)是给定对象的单个构建器,具有用于设置不同参数的流体接口。这是有效Java中提倡的模式,这就是为什么您通常会在Java中看到这一点

如果您有一些非常不同的构建场景,当然,不同的构建器对象可能是有意义的,但我想在这里退一步。如果一个对象中有这么多不同的属性,则可能有一个对象执行了太多操作,问题的一个症状是构建器的复杂性

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

正确设计抽象层的困难使上述内容既具有现实性,又具有理想性,但我认为让对象做太多事情的意义是站得住脚的


在您的汽车示例中,汽车有许多层,但保持其可管理性的诀窍是区分不同的总成,并使较大的汽车由几个总成选项组成,这些选项本身由几个总成选项组成,等等。每个总成都有其自身的目标,而且可能是它自己的构建器。

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

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

以上假设SUV是car的一个子类。如果不是,那么我可以在一个附加函数中指定SUV。

建造者模式当然是自由裁量的,如果它过于复杂,那么它就太复杂了,你可能想考虑一种不同的方法来创建对象,比如。我认为构建器模式在以下几个场景中表现出色:

  • 创建具有多个有效配置的对象
    • 我认为你的汽车就是一个很好的例子
  • 创建不可变的对象,在其中不能预先提供所有必需的数据
    • 例如,查看guava immutable collection builders,了解这方面的一个很好的示例
以下是如何实现汽车制造商的一个示例:

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);
        }
    }
}
构建器不必是嵌套类,但它允许您向可能滥用API的人隐藏构造函数。还要注意,必须提供所需的最小参数,以便创建生成器。您可以这样使用此生成器:

Car car = Car.builder(Color.WHITE, 500).withSunroof().modelName("Mustang").createCar();

好吧,几个月前我也在想同样的问题,我提出了一个新的设计模式,名为
step-builder-pattern
()

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

概念很简单:

  • 在内部类或接口中编写创建步骤,其中每个方法都知道接下来可以显示什么
  • 在内部静态类中实现所有步骤接口
  • 最后一步是构建步骤,负责创建需要构建的对象

  • 我想说一个建筑商可能做得太少了。正如您所说,构建器适合创建复杂对象。一个建设者做的太多了,就是它创建了太多可能的对象。e、 g.系统中的每个对象都可以有一个全局生成器。这可能太多了。这不是一个真正的答案,但很自然,每一种设计模式都可能被过度使用。并不是每件事都能很好地符合模式。如果你有