Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使所有生成器方法成为必需的?_Java - Fatal编程技术网

Java 如何使所有生成器方法成为必需的?

Java 如何使所有生成器方法成为必需的?,java,Java,我正在建设一个汽车类,有发动机,变速箱,离合器等。 我不想要一个包含7个参数的臃肿构造函数,所以我决定使用构建器模式。 所有零件都是必需的。 但是,我如何让Car类的用户使用所有零件的设置器,因为它们都是强制性的? 抛出异常 public class Car { private Engine engine; private Chassis chassis; private GearBox gearBox; private Coupe coupe; pri

我正在建设一个汽车类,有发动机,变速箱,离合器等。 我不想要一个包含7个参数的臃肿构造函数,所以我决定使用构建器模式。 所有零件都是必需的。 但是,我如何让Car类的用户使用所有零件的设置器,因为它们都是强制性的? 抛出异常

public class Car {

    private Engine engine;
    private Chassis chassis;
    private GearBox gearBox;
    private Coupe coupe;
    private Exterior exterior;
    private Interior interior;
    private Clutch clutch;

    public Car(Builder builder) {
        engine = builder.engine;
        chassis = builder.chassis;
        gearBox = builder.gearBox;
        coupe = builder.coupe;
        exterior = builder.exterior;
        interior = builder.interior;
        clutch = builder.clutch;
    }

    public static class Builder {

        private Engine engine;
        private Chassis chassis;
        private GearBox gearBox;
        private Coupe coupe;
        private Exterior exterior;
        private Interior interior;
        private Clutch clutch;


        private Car build() {
            return new Car(this);
        }

        public Builder setEngine(@NonNull Engine engine) {
            this.engine = engine;
            return this;
        }

        public Builder setChassis(@NonNull Chassis chassis) {
            this.chassis = chassis;
            return this;
        }

        public Builder setGearBox(@NonNull GearBox gearBox) {
            this.gearBox = gearBox;
            return this;
        }

        public Builder setCoupe(@NonNull Coupe coupe) {
            this.coupe = coupe;
            return this;
        }

        public Builder setExterior(@NonNull Exterior exterior) {
            this.exterior = exterior;
            return this;
        }

        public Builder setInterior(@NonNull Interior interior) {
            this.interior = interior;
            return this;
        }

        public Builder setClutch(@NonNull Clutch clutchs) {
            this.clutch = clutchs;
            return this;
        }

    }


}
我希望用户调用所有的生成器setter,而不是它们的可选子集。我该怎么做

有没有另一种方法来构造一辆汽车,而不需要一个需要这么多参数的巨大构造器


编辑:我看过了,但没有解决方案可以阻止大型构造函数。

生成器适用于许多部件是可选的,或者您有许多不同的配置的情况。然后,使用
build()
方法确保特定配置有效。HTML生成器是有意义的,字符串生成器则不然

如果没有可选零件,则有以下选项:

  • 摆脱构造器,使用需要所有部件的构造器。经典解决方案,可读性最低且难以扩展

  • 您可以在
    build()
    方法中为每个缺少的部分抛出异常。这需要编写很多代码,而且有点违背了模式

  • 可以向生成器中添加具有多个参数的方法。这是上述两种情况的混合。您仍然在使用构建器模式(以便以后可以轻松添加更多配置),但您的API也可以更清楚地传达哪些部分是必需的

    例如,您将来可能会得到一台发动机,它已经包含齿轮箱或需要一个特定的齿轮箱(即,当您使用此发动机时,您也会选择齿轮箱)。要实现这一点,您需要创建第二个生成器方法,该方法只需要请求引擎并自动确定齿轮箱

  • 使用具有受保护方法的工厂来构建零件。工厂将确保为汽车提供所有零件。如果要替换零件,可以替代受保护的方法。如果您有很多默认设置,并且只有2-3个有用的配置,那么这将非常有效

  • 使用多个构建器。不要用单个螺丝来制造所有东西,而是用更大的积木来制造你的汽车:推进、内饰、车身。使用构造器从这三个参数中创建汽车(三个参数很好)。现在您可以使用三个构建器来创建这三个。尝试在必需元素和可选元素之间找到平衡

  • 链建设者如。这将强制用户填写所有部分(因为您只能调用链末尾的
    build()
    )。这里的主要缺点是:需要编写大量代码,而且从用户角度来看很难遵循,因为代码分布在许多类中。这是一个例子;该项目将SQL语法实现为一个构建器链

记住构建器模式试图解决的问题:它们使添加更多部件变得容易,因为如果新部件是可选的,则所有现有代码都不需要更改。如果新的部分是强制性的,那么构建器模式就成了一种负担:对于一个庞大的(不可读的)构造函数,在所有需要修复的地方都会出现编译错误。在这种情况下,生成器将在运行时失败;您需要一个IDE来找到所有需要修复的地方。

如果您查看,您会发现,有一个重要的东西,您没有,它是Director。以及生成器界面,它定义了所有必需的方法


Director应该调用interface builder上所需的所有方法(在您的案例中为“build engine、build coupe等”)。实现构建器接口的类必须重写所有方法,否则代码甚至无法编译。

如果主要原因是使用流畅的API,而不是删除臃肿的构造函数,则可以将构建器链接在一起:

class Engine {}
class Door {}
class Car {
    Car(Engine engine, Door door) {

    }
}

class CarBuilder {
    private Engine engine;

    public CarWithEngineBuilder withEngine(Engine engine) {
        this.engine = engine;
        return new CarWithEngineBuilder();
    }

    class CarWithEngineBuilder {
        private Door door;


        public CarWithEngineAndDoor withDoor(Door door) {
            this.door = door;
            return new CarWithEngineAndDoor();
        }

        class CarWithEngineAndDoor {
            public Car build() {
                return new Car(engine, door);
            }
        }
    }
}

class TestStuff {
    {
        Car c = new CarBuilder().withEngine(new Engine()).withDoor(new Door()).build();
    }
}

或者,如果您主要关心的是构造函数的大小,那么可能构造函数正在告诉您一些事情,您可以查看该类,看到一些部分在逻辑上是“在一起”的。也就是说,
发动机
齿轮
制动器
是较大部件的一部分吗?汽车是否应该是
驱动系统
底盘
(包括
外部
内部
)。然后
汽车
的构造器有一系列可管理的参数,就像
驱动系统
底盘
一样?

回答得很好,但我想知道的是什么。选项3不会给代码的可读性带来同样的问题吗?我的意思是,你要么把参数加载到构造函数中,要么加载到生成器中的方法中。参数的数量不会改变。因此,选择2保持不变。但这与构建者模式的理念背道而驰,所以在这种情况下,重新考虑整个构建者模式不是最好的选择吗?@J.K:使用构建者模式的原因之一是为了防止丑陋。如果您愿意,我不会让
build()
方法引发异常。一个好的不可变类将在返回新对象之前在其(私有)构造函数中强制其不变量。如果缺少必需的参数,则生成器调用的(私有)构造函数应抛出
IllegalStateException
@MarkSmit:Correct。选项3不是完美的解决方案;如果你想留住建筑商,这是最好的折衷办法。也许他最好有一个工厂,有保护的方法来制造零件。然后他可以覆盖工厂以替换零件。@J.K.用创建发动机、齿轮箱等默认实例的方法替换setter。在
build()
方法中调用它们。factory和builder之间的区别在于,factory知道如何在配置builder时进行构建,因为可能的配置太多。长构造函数很难阅读,更难更改。想象一下所有的地方