有效Java中的生成器模式

时间:2011-02-15 17:48:25

标签: java design-patterns builder-pattern

我最近开始阅读Joshua Bloch撰写的Effective Java。我发现Builder模式的概念[书中的第2项]非常有趣。我试图在我的项目中实现它,但有编译错误。以下是我试图做的事情:

具有多个属性及其构建器类的类:

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

我尝试使用上述类的类:

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

我收到以下编译器错误:

  

包含的封闭实例   effectivejava.BuilderPattern.NutritionalFacts.Builder   是必须的                   NutritionalFacts n = new   NutritionalFacts.Builder(10).carbo(23).fat(1).build();

我不明白这个消息是什么意思。请解释。上面的代码类似于Bloch在他的书中提出的例子。

11 个答案:

答案 0 :(得分:160)

使构建器成为static类。然后它会工作。如果它是非静态的,那么它需要一个拥有它的实例 - 而且要点不是它的实例,甚至禁止在没有构建器的情况下创建实例。

public class NutritionFacts {
    public static class Builder {
    }
}

参考:Nested classes

答案 1 :(得分:27)

You should make the Builder class as static and also you should make the fields final and have getters to get those values. 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();

答案 2 :(得分:13)

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

答案 3 :(得分:11)

您正在尝试以静态方式访问非静态类。将Builder更改为static class Builder,它应该有效。

您提供的示例用法失败,因为没有Builder的实例存在。始终实例化用于所有实际目的的静态类。如果你没有让它静止,你需要说:

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

因为您每次都需要构建一个新的Builder

答案 4 :(得分:7)

您需要将Builder内部类声明为static

查阅non-static inner classesstatic inner classes的一些文档。

基本上,如果没有附加的外部类实例,非静态内部类实例就不存在。

答案 5 :(得分:4)

这意味着您无法创建封闭类型。这意味着首先必须先调用“父”类的实例,然后从此实例中创建嵌套类实例。

NutritionalFacts n = new NutritionalFacts()

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

Nested Classes

答案 6 :(得分:4)

Builder类应该是静态的。我现在没有时间实际测试代码,但如果它不起作用,请告诉我,我会再看看。

答案 7 :(得分:3)

一旦有了想法,实际上,您可能会发现龙目岛的@Builder更方便。

@Builder可让您自动生成使您的类可实例化的代码,例如:

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

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

答案 8 :(得分:1)

当您有2个不同的课程时,我个人更喜欢使用其他方法。所以你不需要任何静态类。这基本上是为了避免在必须创建新实例时写入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);
    }
    // ....
}

因此,您可以像这样使用您的构建器:

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

答案 9 :(得分:0)

正如这里已经提到的,您需要创建类static。 只是很小的补充-如果您愿意,还有一些没有静态方法的方法。

考虑一下。通过在类中声明类似withProperty(value)的类型设置器来实现构建器,并使它们返回对自身的引用。通过这种方法,您可以拥有一个优雅的类,该类安全且简洁。

考虑一下:

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");

查看更多Java Builder示例。

答案 10 :(得分:0)

您需要将 Builder 类更改为静态类Builder 。这样就可以了。