清除对建造者模式的疑虑

时间:2012-10-17 09:11:25

标签: java design-patterns builder

我正在学习构建器模式,到目前为止我理解它,它是用于初始化的常用模式的一个很好的替代方法:

  • 伸缩构造函数模式

  • JavaBean Pattern

问题是,我真的不想从我的域模型中的对象中删除getter和setter。我总是喜欢把它们当作POJO。我不喜欢它的原因之一是: 如果我不使用POJO,那么在使用ORM框架时注释变量并不容易......

所以这是我的疑惑: - 是否可以在不使用静态内部类的情况下实现构建器模式? - 如果我必须使用内部类来使用构建器模式,你认为保持getter和setter是正确的吗? - 我为练习做了一个小例子,我试图避开内部类。 你能告诉我你怎么看待它?

产品

    public class Product
{
    private String color;
    private int price;

    public Product() {
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public String toString() {
        return getColor() + "\n" + getPrice();
    }    
}

生成器

public class Builder
{
    private Product product;

    public Builder() {
        product = new Product();
    }
    public Builder withColor(String color) {        
        product.setColor(color);
        return this;
    }

     public Builder withPrice(int price) {        
        product.setPrice(price);
        return this;
    }
    public Product build() {
        return product;
    }
}**

客户端

public class Client
{

    public static void main(String[] args) {
        System.out.println(new Builder().withColor("Black").withPrice(11).build());
        System.out.println("-----------------------------------------------------");
        System.out.println(new Builder().withColor("Blue").withPrice(12).build());
    }
}

enter image description here

8 个答案:

答案 0 :(得分:12)

Builder模式对于创建不可变对象很有用,并且避免使用带有可选参数的多个构造函数。

IMO使用Builder模式构建可以使用setter更新的POJO是没用的。您只需创建一个附加类。

根据使用的ORM框架,可能不需要存在setter方法。但只能通过反思来分配成员价值。

产品类别:

public final class Product {
    private final String color;
    private final int price;

    public Product(Builder builder) {
        this.color = builder.getColor();
        this.price = builder.getPrice();
    }

    public String getColor() {
        return color;
    }

    public int getPrice() {
        return price;
    }

    public String toString() {
        return getColor() + "\n" + getPrice();
    }    
}

构建器类:

public final class Builder {

    private String color;
    private int price;

    public Builder() {
        // Assign any default values
    }

    public Builder color(String color) {        
        this.color = color;
        return this;
    }

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

    protected String getColor() {
        return color;
    }

    protected int getPrice() {
        return price;
    }

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

答案 1 :(得分:4)

构建器模式在不可变对象的上下文中最有用。根据定义,不可变对象没有setter。所以他们所有的属性都必须被压缩到构造函数中。这是构建器模式派上用场的地方。它允许您将复杂的不可变对象的初始化拆分为多个自解释指令,这样您就不需要在您的代码中使用像这个虚构示例的构造函数调用,您无法分辨哪个参数执行了什么:

Thing foo = new Thing(1, 125, Thing.SOMETHING, new Whatchamacallit(17, 676), getStuffManager(StuffManager.ZOMG), true, false, false, maybe);

我没有发现当创建的对象是可变的时,Builder模式会创建任何重要的值。您通过构建器执行的所有操作也可以直接使用创建的对象完成。

另外,我认为上面的程序不是构建器模式的教科书示例。您通常不会通过将构建器传递给构造函数来创建对象。您可以通过在构建器上调用create方法来创建对象,然后将其所有属性传递给对象的构造函数。这种模式的优点是它使构建器有机会检查其内部状态的一致性,并可能在开始构建对象之前抛出异常。

java StringBuilder类是一个很好的例子(在这种情况下,创建方法为tostring)。

答案 2 :(得分:3)

在这里使用建筑师实际上有什么收获?

我无法看到:您可以直接创建新产品并在其上使用getter和setter。如果你有一个简单的POJO那么绝对没有错:

Product p=new Product();
p.setColour("Black");
p.setPrice(11);
doSomethingWith(p);

保存一些输入字符是恕我直言,不值得引入新的类/构建器抽象。

构建器在以下情况下更有用:

  • 您想要创建不可变对象(因此无法使用setter)
  • 如果你有复杂的工厂逻辑,不能用简单的getter和setter表达,或者你想以不同的方式重复使用
  • (偶尔)当您希望代码库的不同部分由于某种原因配置构建器的不同方面时。

答案 3 :(得分:1)

构建器模式非常适合生成不可变类,但它仍然是可变类的一个很好的选择。

在任何对象包含许多需要在构造期间设置的字段的情况下,构建器是一种合理的设计选择;特别是如果可以为几个值选择合理的默认值。

您是否使用内部课程取决于您的目标。如果希望通过构建器强制构造,可以将构建器定义为内部类,并确保外部类只有一个私有构造函数。

答案 4 :(得分:1)

以下是GoF书中的Builder合作: 合作 1.客户端创建Director对象并使用所需的Builder对象对其进行配置。 2.每当构建产品的一部分时,主任都会通知建筑商。 3. Builder处理来自导演的请求并添加产品的部件。 3.客户端从构建器中检索产品。

Builder模式侧重于逐步构建复杂对象。 Builder将产品作为最后一步返回。在没有setter的情况下返回的类可能与immutable一样好。使用setter,它可以被修改。内部课程有助于掩盖细节。

另一点值得注意的是,创作设计模式背后的主要动机是客户并不担心创建产品。对象创建过程委托给工厂,构建器等。客户端不必担心对象创建。它将指定它想要的内容,并将根据委派的创建过程获得它。

答案 5 :(得分:1)

  

是否可以在不使用静态的情况下实现构建器模式   内在的课程?

当然,是的。就Builder设计模式而言,如果Builder是内部类,它没有任何区别。

  

- 如果我必须使用内部类来使用构建器模式,你认为保持getter和setter是正确的吗?

是的,没关系。它有点像,使用某个模板构建对象,然后根据需要自定义它。

  

- 我为练习做了一个小例子,我试图避开内心   类。你能告诉我你对它的看法吗?

两个问题 -

  1. 该示例不能证明使用Builder模式。构建器模式用于构建复杂对象。因此,如果您可以简单地将产品构建为: Product p = new Product(); p.setColor(c); p.setPrice(prc); 然后你所展示的方式几乎没有任何好处。
  2. Product不应依赖Builder

答案 6 :(得分:1)

我发现自己在考虑吸气剂是否对建造者有益。构建器通常不应用作返回值 - 单个方法或类应负责使用构建器创建实体。因此,方法(或类)应保留所需的信息,而不是将其取回。

出于这些原因,我决定不在构建器类中使用任何getter。 Builder只有setter(可以是带有Abc(...),setAbc(...)或abc(...)),build()以及一些私有方法,比如validate()。

使用类Product,示例实体如下所示:

class Product {
    private final String color;
    private final int price;

    private Product(ProductBuilder builder) {
        this.color = builder.color;
        this.price = builder.price;
    }

    // equals, hashCode, toString

    public builder() {
        return new ProductBuilder(this);
    }

    public static emptyBuilder() {
        return new ProductBuilder();
    }

    public String getColor() {
        return color;
    }

    public int getPrice() {
        return price;
    }

现在,构建器类是实体的内部类,它允许我使用私有构造函数。

    public static class ProductBuilder {
        private String color;
        private int price;

        private ProductBuilder() {
        }

        private ProductBuilder(Product entity) {
            this.color = entity.color;
            this.price = entity.price;
        }

        public ProductBuilder withColor(String color) {
            this.color = color;
            return this;
        }

        public ProductBuilder withPrice(int price) {
            this.price = price;
            return this;
        }

        public Product build() {
            return new Product(this.validate());
        }

        private ProductBuilder validate() {
            if (color == null) {
                throw new IllegalStateException("color is null");
            }
            return this;
    }
}

正如您所看到的,我添加了方法builder()以将构建器作为实例的副本,并将emptyBuilder()作为工厂方法来隐藏构造函数(可能有更好的名称)。< / p>

此外,在构造不可变类时,请确保内部的所有内容也是不可变的。收藏很棘手,你必须复制,然后在其上使用Collections.unmodifiable *(...),以确保没有人对不可修改的包装下的集合有引用。

编辑:据说,如果你有抽象的超类,你需要 getter。这是夸大其辞。如果你有一个包含所有参数的构造函数,你只需要它。如果您像我一样通过构建器,则会得到:

class Product extends Something { ...
    private Product(ProductBuilder builder) {
        super(builder); // that one must be protected, not private
        ...
    }
    public static class ProductBuilder extends SomethingBuilder { ...
        protected ProductBuilder validate() {
            super.validate();
            ...
        }
    }
}

那么,我们需要 getter吗?这一次,不是真的。没有他们,我们仍然没事。其他一些想法?

答案 7 :(得分:0)

Builder 是关于几件事的,您可能只想使用一个方面:流畅的API 。只需更改设置者即可返回this而不是void,您可以最大限度地满足您的需求。然后你可以使用chained-setter习语:return new MyBean().setCheese(cheese).setBacon(bacon);

另外,术语“POJO”与“JavaBean”并不相同。事实上,有时这两个术语被用作对立面。 POJO的意义在于它不符合而不是Java对象。例如,它可能使用public个变量。