简体   繁体   English

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

[英]Is a nested Builder class really necessary as described in Effective Java?

So, in the famous Effective Java book, it introduces a Builder pattern where you can have an inner static Builder class to instantiate a class. 因此,在着名的Effective Java书中,它引入了一个Builder模式 ,您可以在其中使用内部静态Builder类来实例化一个类。 The book suggests the following design of a class: 这本书建议了以下类的设计:

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

However I have failed to understand why do we really need an inner Builder class ? 但是我无法理解为什么我们真的需要一个内部的Builder class The above code have duplicate lines for field declarations (int a, b), this would become relatively messy if we had more fields. 上面的代码有字段声明的重复行(int a,b),如果我们有更多字段,这将变得相对混乱。

Why not just get rid of the Builder class, and let Example class take on all the set methods that were in Builder class? 为什么不摆脱的Builder类,并让Example类采取对所有set那名的方法Builder类?

So to instantiate Example , it would become Example e = new Example().a(3).b.(3); 因此,要实例化Example ,它将成为Example e = new Example().a(3).b.(3); instead of Example e = new Example.Builder.a(3).b(3).build(); 代替Example e = new Example.Builder.a(3).b(3).build();


NOTE: The book suggests this pattern for classes that have a long list of parameters to be set. 注意:本书为具有很长参数列表的类建议了这种模式。

Builder is a pattern for the construction of complex objects. Builder是构建复杂对象的模式。 I wouldn't count your example as complex; 我不认为你的榜样很复杂; indeed, the builder adds an awful lot of unnecessary code, rather than just using constructor parameters. 实际上,构建器添加了大量不必要的代码,而不仅仅是使用构造函数参数。

There are a few reasons why you'd want to use a builder: 您想要使用构建器的原因有几个:

  • To construct complex immutable objects. 构造复杂的不可变对象。 Immutable objects need to have final (or logically final) fields, so they must be set at construction time. 不可变对象需要具有最终(或逻辑上最终)字段,因此必须在构造时设置它们。

    Let's say that you have N fields, but you only want to explicitly set some of them in certain use cases. 假设您有N个字段,但您只想在某些用例中明确设置其中的一些字段。 You would need up to 2^N constructors to cover all of the cases - known as "telescoping", since the length of the parameter list gets longer and longer. 您需要最多2 ^ N个构造函数来覆盖所有情况 - 称为“telescoping”,因为参数列表的长度变得越来越长。 The builder allows you to model optional parameters: if you don't want to set that parameter, don't call that setter method. 构建器允许您为可选参数建模:如果您不想设置该参数,请不要调用该setter方法。

  • To allow self-documentation of the parameters' meaning. 允许自我记录参数的含义。 By naming the setter methods appropriately, you can see what the values mean at a glance. 通过适当地命名setter方法,您可以一目了然地看到值的含义。

    It also helps to verify that you are not accidentally reversing parameters of the same type, since you can see what each value is used for. 它还有助于验证您是否意外地反转了相同类型的参数,因为您可以看到每个值的用途。

If the fields in the outer class are final, then the builder is necessary if you want to incrementally specify parameter values, as all fields must be initialized in the constructor. 如果外部类中的字段是final,那么如果要逐步指定参数值,则必须使用构建器,因为必须在构造函数中初始化所有字段。

The builder inner class allows fields to be initialized incrementally. 构建器内部类允许以递增方式初始化字段。

As others have pointed out, this applies to immutable objects as well. 正如其他人所指出的那样,这也适用于不可变对象。 The fields don't need to be final; 这些领域不一定是最终的; they effectively will be if no setters are supplied in the outer class. 如果在外层没有提供制定者,它们将是有效的。

The builder can possibly accumulate the parameters more efficiently than direct construction. 构建器可以比直接构造更有效地累积参数。 Consider the StringBuilder . 考虑一下StringBuilder It allocates a temporary buffer to accumulate partial results. 它分配一个临时缓冲区来累积部分结果。 The "build" operation in its case is toString() . 其案例中的“构建”操作是toString()

Finally, there could be things you just can't do in the constructor of a class. 最后,可能有一些你在类的构造函数中无法做到的事情。 If you need to pass a value to a super constructor, but that value is not part of the arguments to your constructor, it may not be possible to, since you must call super() first, and you might not be able to create the argument(s) as a simple expression inside the super(...) call. 如果您需要将值传递给超级构造函数,但该值不是构造函数的参数的一部分,则可能无法,因为您必须首先调用super() ,并且您可能无法创建argument(s)作为super(...)调用中的一个简单表达式。 The BoxLayout comes to mind. 想到BoxLayout。 You pass the JPanel to the BoxLayout constructor. 您将JPanel传递给BoxLayout构造函数。 You pass the layout to the JPanel constructor. 您将布局传递给JPanel构造函数。 Chicken and egg. 鸡肉和鸡蛋。 And this code is not allowed, because this is not yet constructed. 而这种代码是不允许的,因为this尚未构成。

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

Fortunately, a JPanel is not immutable; 幸运的是, JPanel不是一成不变的; you can set the layout after construction. 你可以在施工后设置布局。

The rationale is for complicated classes. 理由是复杂的课程。 Notice that the Builder object returns itself, so one can do chaining, such as: 请注意, Builder对象返回自身,因此可以进行链接,例如:

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

Apache uses this approach in some cases to allow the incremental setting of various values. 在某些情况下,Apache使用此方法来允许增量设置各种值。 It also allows the .build() method to do some checking of all of the correct values to make an object if desired. 它还允许.build()方法对所有正确的值进行一些检查,以便在需要时创建对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM