首页 文章

如何使所有Builder方法都需要?

提问于
浏览
5

我正在构建一个具有引擎,变速箱,离合器等的汽车类 . 我不想要一个带有7个参数的膨胀构造函数,因此我决定使用构建器模式 . 所有部件都是必需的 . 但是,如何让Car类的用户使用所有部件的setter,因为它们都是强制性的?抛出异常?

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;
        }

    }


}

我希望用户调用所有构建器设置器,而不是它们的可选子集 . 我怎么做?

有没有另一种方法来构建一辆汽车而没有一个需要这么多参数的庞大构造函数?

编辑:我看了The builder pattern and a large number of mandatory parameters但没有解决方案阻止巨大的构造函数 .

3 回答

  • 6

    如果你看一下builder pattern,你会发现,有一个重要的东西,你没有,而且是导演 . 还有Builder接口,它定义了所有必需的方法 .

    Director应调用接口构建器所需的所有方法(在您的情况下为“构建引擎,构建coupe等”) . 实现Builder接口的类必须覆盖所有方法,否则代码甚至无法编译 .

  • 4

    构建器适用于许多部件是可选的或您有许多不同配置的东西 . 然后 build() 方法确保特定配置有效 . HTML构建器是有意义的,字符串构建器不是那么多 .

    如果没有可选部件,您可以选择以下选项:

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

    • 您可以在 build() 方法中为每个缺失的部分抛出异常 . 这是很多编写的代码,有点违背模式 .

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

    例如,您可能会在未来获得一个已经包含变速箱或需要特定变速箱的发动机(即,当您使用此发动机时,您也可以选择变速箱) . 要实现这一点,您需要创建第二个构建器方法,该方法只需要引擎并自动确定变速箱 .

    • 使用具有构建零件的受保护方法的工厂 . 工厂将确保所有零件都供应给汽车 . 如果要替换零件,可以覆盖受保护的方法 . 如果你有很多默认值,只有2-3个有用的配置,这很有效 .

    • 使用多个构建器 . 而不是用单独的螺丝制造一切,用更大的积木来制造你的汽车:推进,内饰,车身 . 使用构造函数从这三个中创建您的汽车(三个参数很好) . 现在,您可以使用三个构建器来创建这三个 . 尝试在必需元素和可选元素之间找到 balancer 点 .

    • 连锁建设者为explained by mlk below . 这会强制用户填写所有部分(因为您只能在链的末尾调用 build() ) . 这里的主要缺点是:由于代码分布在许多类中,因此从用户的角度来看,需要编写很多代码并且难以遵循 . jOOQ就是一个例子;该项目将SQL语法实现为构建器链 .

    记住构建器模式试图解决的问题:它们可以轻松添加更多部分,因为所有现有代码都不需要更改 if the new part is optional . 如果新部分是必需的,那么构建器模式将成为一种负担:使用巨大的(不可读的)构造函数,您会在需要修复的所有位置获得编译错误 . 在这种情况下,构建器将在运行时失败;你需要一个IDE来找到所有需要修复的地方 .

  • 0

    如果主要原因是使用流畅的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();
        }
    }
    

    或者如果您的主要关注点是构造函数的大小,也许构造函数会告诉您某些内容,您可以查看该类,并查看某些部分是逻辑上的"together" . 即是 EngineGearsBrake 是较大组件的一部分?该车应该是 DriveSystemChassis (包括 ExteriorInterior ) . 然后 Car 的构造函数具有可管理的参数数量, DriveSystemChassis

相关问题