如何改进生成器模式?问题

2022-08-31 23:57:21

动机

最近,我搜索了一种在不向构造函数传递大量参数的情况下初始化复杂对象的方法。我尝试了生成器模式,但我不喜欢这样一个事实,即我无法在编译时检查我是否真的设置了所有需要的值。

传统建筑商模式

当我使用生成器模式创建我的对象时,创建更加“类型安全”,因为它更容易看到参数的用途:Complex

new ComplexBuilder()
        .setFirst( "first" )
        .setSecond( "second" )
        .setThird( "third" )
        ...
        .build();

但现在我有了一个问题,我很容易错过一个重要的参数。我可以在方法内部检查它,但这只是在运行时。在编译时,如果我错过了什么,没有什么会警告我。build()

增强的生成器模式

现在我的想法是创建一个构建器,如果我错过了所需的参数,它会“提醒”我。我的第一次尝试如下所示:

public class Complex {
    private String m_first;
    private String m_second;
    private String m_third;

    private Complex() {}

    public static class ComplexBuilder {
        private Complex m_complex;

        public ComplexBuilder() {
            m_complex = new Complex();
        }

        public Builder2 setFirst( String first ) {
            m_complex.m_first = first;
            return new Builder2();
        }

        public class Builder2 {
            private Builder2() {}
            Builder3 setSecond( String second ) {
                m_complex.m_second = second;
                return new Builder3();
            }
        }

        public class Builder3 {
            private Builder3() {}
            Builder4 setThird( String third ) {
                m_complex.m_third = third;
                return new Builder4();
            }
        }

        public class Builder4 {
            private Builder4() {}
            Complex build() {
                return m_complex;
            }
        }
    }
}

如您所见,生成器类的每个 setter 返回不同的内部生成器类。每个内部构建器类只提供一个 setter 方法,最后一个类仅提供一个 build() 方法。

现在,对象的构造再次如下所示:

new ComplexBuilder()
    .setFirst( "first" )
    .setSecond( "second" )
    .setThird( "third" )
    .build();

...但是没有办法忘记所需的参数。编译器不会接受它。

可选参数

如果我有可选参数,我会使用最后一个内部生成器类来设置它们,就像“传统”生成器一样,返回自身。Builder4

问题

  • 这是众所周知的模式吗?它有特殊的名字吗?
  • 你看到任何陷阱吗?
  • 你有什么想法来改进实现 - 从更少的代码行数的意义上?

答案 1

传统的生成器模式已经处理了这个问题:只需在构造函数中采用必需的参数即可。当然,没有什么可以阻止调用方传递 null,但您的方法也不会。

我看到你的方法的一个大问题是,你要么有一个具有强制参数数量的类的组合爆炸,要么强迫用户在一个特定的 sqeuence 中设置参数,这很烦人。

此外,这还有很多额外的工作。


答案 2

不,这并不新鲜。你实际上正在做的是通过扩展标准构建器模式来支持分支来创建一种DSL,这是确保构建器不会产生一组与实际对象冲突的设置的绝佳方法。

就个人而言,我认为这是对构建器模式的一个很好的扩展,您可以使用它做各种有趣的事情,例如在工作中,我们有DSL构建器用于我们的一些数据完整性测试,这使我们能够做这样的事情。好吧,也许不是最好的例子,但我认为你明白了。assertMachine().usesElectricity().and().makesGrindingNoises().whenTurnedOn();


推荐