简体   繁体   English

有效 Java 中的构建器模式

[英]Builder Pattern in Effective Java

I have recently started to read Effective Java by Joshua Bloch.我最近开始阅读 Joshua Bloch 的 Effective Java。 I found the idea of the Builder pattern [Item 2 in the book] really interesting.我发现 Builder 模式的想法 [书中的第 2 项] 非常有趣。 I tried to implement it in my project but there were compilation errors.我试图在我的项目中实现它,但出现编译错误。 Following is in essence what I was trying to do:以下本质上是我试图做的事情:

The class with multiple attributes and its builder class:具有多个属性的类及其构建器类:

public class NutritionalFacts {
    private int sodium;
    private int fat;
    private int carbo;

    public class Builder {
        private int sodium;
        private int fat;
        private int carbo;

        public Builder(int s) {
            this.sodium = s;
        }

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

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

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

    private NutritionalFacts(Builder b) {
        this.sodium = b.sodium;
        this.fat = b.fat;
        this.carbo = b.carbo;
    }
}

Class where I try to use the above class:我尝试使用上述类的类:

public class Main {
    public static void main(String args[]) {
        NutritionalFacts n = 
            new NutritionalFacts.Builder(10).carbo(23).fat(1).build();
    }
}

I am getting the following compiler error:我收到以下编译器错误:

an enclosing instance that contains effectivejava.BuilderPattern.NutritionalFacts.Builder is required NutritionalFacts n = new NutritionalFacts.Builder(10).carbo(23).fat(1).build();需要包含有效java.BuilderPattern.NutritionalFacts.Builder 的封闭实例 NutritionalFacts n = new NutritionalFacts.Builder(10).carbo(23).fat(1).build();

I do not understand what the message means.我不明白消息的含义。 Please explain.请解释。 The above code is similar to the example suggested by Bloch in his book.上面的代码类似于 Bloch 在他的书中提出的例子。

Make the builder a static class.使构建器成为static类。 Then it will work.然后它将起作用。 If it is non-static, it would require an instance of its owning class - and the point is not to have an instance of it, and even to forbid making instances without the builder.如果它是非静态的,它将需要其所属类的实例 - 关键是不要拥有它的实例,甚至禁止在没有构建器的情况下创建实例。

public class NutritionFacts {
    public static class Builder {
    }
}

Reference: Nested classes参考: 嵌套类

You should make the Builder class as static and also you should make the fields final and have getters to get those values.您应该将 Builder 类设为静态,并且您应该将字段设为 final 并使用 getter 来获取这些值。 Don't provide setters to those values.不要为这些值提供设置器。 In this way your class will be perfectly immutable.这样,您的类将是完全不可变的。

public class NutritionalFacts {
    private final int sodium;
    private final int fat;
    private final int carbo;

    public int getSodium(){
        return sodium;
    }

    public int getFat(){
        return fat;
    }

    public int getCarbo(){
        return carbo;
    }

    public static class Builder {
        private int sodium;
        private int fat;
        private int carbo;

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

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

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

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

    private NutritionalFacts(Builder b) {
        this.sodium = b.sodium;
        this.fat = b.fat;
        this.carbo = b.carbo;
    }
}

And now you can set the properties as follows:现在您可以按如下方式设置属性:

NutritionalFacts n = new NutritionalFacts.Builder().sodium(10).carbo(15).
fat(5).build();

要在 Intellij IDEA 中生成内部构建器,请查看此插件: https ://github.com/analytically/innerbuilder

You are trying access a non-static class in a static way.您正在尝试以静态方式访问非静态类。 Change Builder to static class Builder and it should work.Builder更改为static class Builder ,它应该可以工作。

The example usage you give fails because there is no instance of Builder present.您提供的示例用法失败,因为不存在Builder实例。 A static class for all practical purposes is always instantiated.用于所有实际目的的静态类总是被实例化。 If you don't make it static, you'd need to say:如果你不让它静态,你需要说:

Widget = new Widget.Builder(10).setparm1(1).setparm2(3).build();

Because you would need to construct a new Builder every time.因为您每次都需要构建一个新的Builder

You need to declare the Builder inner class as static .您需要将Builder内部类声明为static

Consult some documentation for both non-static inner classes and static inner classes .查阅一些关于非静态内部类静态内部类的文档。

Basically the non-static inner classes instances cannot exist without attached outer class instance.基本上,如果没有附加的外部类实例,非静态内部类实例就不能存在。

Once you've got an idea, in practice, you may find lombok's @Builder much more convenient.一旦有了想法,在实践中,您可能会发现 lombok 的@Builder更加方便。

@Builder lets you automatically produce the code required to have your class be instantiable with code such as: @Builder允许您自动生成使您的类可以使用以下代码实例化所需的代码:

Person.builder()
  .name("Adam Savage")
  .city("San Francisco")
  .job("Mythbusters")
  .job("Unchained Reaction")
 .build(); 

Official documentation: https://www.projectlombok.org/features/Builder官方文档: https ://www.projectlombok.org/features/Builder

This mean that you cant create enclose type.这意味着您不能创建封闭类型。 This mean that first you have to cerate a instance of "parent" class and then from this instance you can create nested class instances.这意味着首先你必须创建一个“父”类的实例,然后从这个实例中你可以创建嵌套的类实例。

NutritionalFacts n = new NutritionalFacts()

Builder b = new n.Builder(10).carbo(23).fat(1).build();

Nested Classes 嵌套类

The Builder class should be static. Builder 类应该是静态的。 I don't have time right now to actually test the code beyond that, but if it doesn't work let me know and I'll take another look.我现在没有时间实际测试除此之外的代码,但如果它不起作用,请告诉我,我会再看看。

I personally prefer to use the other approach, when you have 2 different classes.当您有 2 个不同的类时,我个人更喜欢使用另一种方法。 So you don't need any static class.所以你不需要任何静态类。 This is basically to avoid write Class.Builder when you has to create a new instance.这基本上是为了避免在必须创建新实例时编写Class.Builder

public class Person {
    private String attr1;
    private String attr2;
    private String attr3;

    // package access
    Person(PersonBuilder builder) {
        this.attr1 = builder.getAttr1();
        // ...
    }

    // ...
    // getters and setters 
}

public class PersonBuilder (
    private String attr1;
    private String attr2;
    private String attr3;

    // constructor with required attribute
    public PersonBuilder(String attr1) {
        this.attr1 = attr1;
    }

    public PersonBuilder setAttr2(String attr2) {
        this.attr2 = attr2;
        return this;
    }

    public PersonBuilder setAttr3(String attr3) {
        this.attr3 = attr3;
        return this;
    }

    public Person build() {
        return new Person(this);
    }
    // ....
}

So, you can use your builder like this:因此,您可以像这样使用您的构建器:

Person person = new PersonBuilder("attr1")
                            .setAttr2("attr2")
                            .build();

As many already stated here you need to make the class static .正如许多人在这里已经说过的那样,您需要将类设为static Just small addition - if you want, there is a bit different way without static one.只是一个小小的补充——如果你愿意,没有静态的有一点不同的方式。

Consider this.考虑一下。 Implementing a builder by declaring something like withProperty(value) type setters inside the class and make them return a reference to itself.通过在类中声明诸如withProperty(value)类型设置器之类的东西来实现构建器,并使它们返回对自身的引用。 In this approach, you have a single and an elegant class which is a thread safe and concise.在这种方法中,您有一个单一且优雅的类,它是线程安全且简洁的。

Consider this:考虑一下:

public class DataObject {

    private String first;
    private String second;
    private String third;

    public String getFirst(){
       return first; 
    }

    public void setFirst(String first){
       this.first = first; 
    }

    ... 

    public DataObject withFirst(String first){
       this.first = first;
       return this; 
    }

    public DataObject withSecond(String second){
       this.second = second;
       return this; 
    }

    public DataObject withThird(String third){
       this.third = third;
       return this; 
    }
}


DataObject dataObject = new DataObject()
     .withFirst("first data")
     .withSecond("second data")
     .withThird("third data");

Check it out for more Java Builder examples.查看更多Java Builder示例。

You need to change Builder class to static class Builder .您需要将Builder类更改为静态类 Builder Then it will work fine.然后它会正常工作。

The other solutions double the memory allocation to instantiate the object.其他解决方案将内存分配加倍以实例化对象。 The following solution does not have that problem.以下解决方案不存在该问题。

public class NutritionalFacts{

    private int sodium;
    private int fat;
    private int carbo;

    private NutritionalFacts(){}

    public int getSodium(){ return sodium;}

    public int getFat(){ return fat;}

    public int getCarbo(){ return carbo;}

    public static class Builder{
        private NutritionalFacts nutrionalFacts;

        public Builder(){
           nutrionalFacts = new NutritionalFacts();
        }

        public Builder sodium(int s){
            nutrionalFacts.sodium = s;
            return this;
        }

        public Builder fat(int f){
            nutrionalFacts.fat = f;
            return this;
        }

        public Builder carbo(int c){
            nutrionalFacts.carbo = c;
            return this;
        }

        public NutritionalFacts build(){
            return nutrionalFacts;
        }
    }
}

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

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