8

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

  • 伸缩构造函数模式

  • JavaBean 模式

问题是,我真的不喜欢从我的域模型中的对象中删除 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());
    }
}

在此处输入图像描述

4

8 回答 8

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);
    }
}
于 2012-10-17T09:22:53.477 回答
4

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

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

当创建的对象是可变的时,我发现 Builder 模式不会创建任何重要的值。您通过构建器所做的一切也可以直接使用创建的对象来完成。

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

java StringBuilder类就是一个很好的例子(在这种情况下是 create 方法tostring)。

于 2012-10-17T09:22:20.277 回答
3

在这里使用构建器实际上能为您带来什么?

据我所知,什么都没有:您可以创建一个新产品并直接在其上使用 getter 和 setter。如果你有一个简单的 POJO,那么绝对没有错:

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

恕我直言,节省一些打字字符不值得引入新的类/构建器抽象。

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

  • 您想创建一个不可变对象(因此不能使用 setter)
  • 如果您有复杂的工厂逻辑,无法用简单的 getter 和 setter 轻松表达,或者您想以不同的方式重用
  • (偶尔)当您出于某种原因想要让代码库的不同部分配置构建器的不同方面时。
于 2012-10-17T09:23:11.780 回答
1

构建器模式非常适合生成不可变类,但对于可变类来说它仍然是一个不错的选择。

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

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

于 2012-10-17T09:21:58.857 回答
1

以下是 GoF 书中的 Builder 协作: 协作 1. 客户端创建 Director 对象并使用所需的 Builder 对象对其进行配置。2. 每当需要构建产品的一部分时,Director 会通知构建者。3. Builder 处理来自director 的请求,并将部件添加到产品中。3. 客户从建造者那里取回产品。

建造者模式专注于一步一步地构造一个复杂的对象。Builder 作为最后一步返回产品。在没有设置器的情况下返回的类可能与不可变一样好。使用setter,它可以被修改。内部类有助于掩盖细节。

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

于 2012-10-17T09:53:32.217 回答
1

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

绝对没错。就 Builder 设计模式而言,Builder 是否是内部类没有任何区别。

- 如果我必须通过使用内部类来使用构建器模式,您认为保留 getter 和 setter 是否正确?

是的,没关系。有点像,使用某个模板构建对象,然后在需要时对其进行自定义。

- 我做了一个小例子来练习,我试图避免内部类。你能告诉我你怎么看吗?

两个问题——

  1. 该示例不能证明使用 Builder 模式的合理性。构建器模式用于构建复杂的对象。因此,如果您可以简单地将产品构建为: Product p = new Product(); p.setColor(c); p.setPrice(prc); 那么您所展示的方式几乎没有任何好处。
  2. Product不应该依赖于Builder.
于 2012-10-19T18:18:52.683 回答
1

我发现自己在想,getter 在构建器中是否有用。通常不应将构建器用作返回值 - 单个方法或类应负责使用构建器创建实体。因此,方法(或类)应该保留它需要的信息,而不是取回它。

由于这些原因,我决定不在构建器类中使用任何 getter。Builder 只有设置器(无论是 withAbc(...)、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()作为工厂方法来隐藏构造函数(也许它有更好的名称)。

此外,在构造不可变类时,请确保其中的所有内容也是不可变的。集合很棘手,您必须制作一个副本,然后在其上使用 Collections.unmodifiable*(...) 以确保没有人引用位于不可修改包装器下的集合。

编辑:据称,如果你有抽象超类,你需要吸气剂。这是夸大其词。仅当您有一个具有所有参数的构造函数时才需要它。如果你像我一样通过构建器,你会得到:

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();
            ...
        }
    }
}

那么,我们需要吸气剂吗?这一次,真的不是。没有他们我们仍然很好。其他一些想法?

于 2015-02-04T13:03:47.117 回答
0

Builder是关于几件事的,你可能只想利用一个方面:fluent API。您只需将设置器更改为 returnthis而不是void. 然后你可以使用chained-setter成语:return new MyBean().setCheese(cheese).setBacon(bacon);

附带说明一下,术语“POJO”与“JavaBean”的含义不同。事实上,有时这两个术语被用作对立面。POJO 的意义在于它除了作为 Java 对象之外不符合其他任何东西。例如,它可以使用public变量。

于 2012-10-17T09:31:52.277 回答