如Effective Java中所述,嵌套的Builder类是否真的有必要?

时间:2016-04-05 05:47:35

标签: java design-patterns builder

因此,在着名的 Effective Java 一书中,它引入了一个 Builder模式,您可以在其中使用内部静态Builder类来实例化一个类。这本书建议了以下类的设计:

public class Example {
    private int a;
    private int b;

    public static class Builder() {
        private int a;
        private int b;

        public Builder a(int a) {
            this.a = a;
            return this;
        }

        public Builder b(int b) {
            this.b = b;
            return this;
        }

        public Example build() {
            return new Example(this);    
        }
    }

    private Example(Builder builder) {
        this.a = builder.a;
        this.b = builder.b;
    }
}

但是我没理解为什么我们真的需要一个内部Builder class上面的代码有重复的字段声明行(int a,b),这会变得相对凌乱如果我们有更多的领域。

为什么不摆脱Builder课程,让Example课程采用set课程中的所有Builder方法?

因此要实例化Example,它将变为Example e = new Example().a(3).b.(3);而不是Example e = new Example.Builder.a(3).b(3).build();

注意:本书为具有较长参数列表的类建议了这种模式。

3 个答案:

答案 0 :(得分:10)

Builder是构建复杂对象的模式。我不认为你的榜样很复杂;实际上,构建器添加了大量不必要的代码,而不仅仅是使用构造函数参数。

您想要使用构建器的原因有几个:

  • 构造复杂的不可变对象。不可变对象需要具有最终(或逻辑上最终)字段,因此必须在构造时设置它们。

    假设您有N个字段,但您只想在某些用例中明确设置其中的一些字段。您需要最多2 ^ N个构造函数来覆盖所有情况 - 称为“telescoping”,因为参数列表的长度变得越来越长。构建器允许您为可选参数建模:如果您不想设置该参数,请不要调用该setter方法。

  • 允许自我记录参数的含义。通过适当地命名setter方法,您可以一目了然地看到值的含义。

    它还有助于验证您是否意外地反转了相同类型的参数,因为您可以看到每个值的用途。

答案 1 :(得分:1)

如果外部类中的字段是final,那么如果要逐步指定参数值,则构建器是必需的,因为必须在构造函数中初始化所有字段。

构建器内部类允许以递增方式初始化字段。

正如其他人所指出的那样,这也适用于不可变对象。这些领域不需要是最终的;如果在外层没有提供制定者,它们将是有效的。

构建器可以比直接构造更有效地累积参数。考虑StringBuilder。它分配一个临时缓冲区来累积部分结果。 "构建"在这种情况下的操作是toString()

最后,你可以在类的构造函数中做些事情。如果您需要将值传递给超级构造函数,但该值不是构造函数的参数的一部分,则可能无法实现,因为您必须先调用super(),否则您可能无法在super(...)调用中将参数创建为简单表达式。想到BoxLayout。您将JPanel传递给BoxLayout构造函数。您将布局传递给JPanel构造函数。鸡肉和鸡蛋。并且不允许使用此代码,因为尚未构造this

class X extends JPanel {
    X() {
        super( new BoxLayout(this) );   // Error: Cannot use "this" yet
    }
}

幸运的是,JPanel不是一成不变的;你可以在施工后设置布局。

答案 2 :(得分:1)

理由是复杂的课程。请注意,Builder对象返回自身,因此可以进行链接,例如:

Example exp = Example.Builder().a(5).b(10).build();

Apache在某些情况下使用此方法来允许增量设置各种值。它还允许.build()方法检查所有正确的值,以便在需要时创建对象。