Clearing doubts about the builder pattern

别说谁变了你拦得住时间么 提交于 2019-12-18 11:33:51

问题


I am learning about the builder pattern, and so far I understood that, it is a great alternative to the commonly patterns used for initialization:

  • Telescoping Constructor Pattern

  • JavaBean Pattern

The thing is, I don't really like to remove the getters and setters from the objects in my domain model. I always like to keep them as POJOs. One of the reasons I don't like it is: If i don't use POJOs, then it is not easy to annotate the variables when using ORM frameworks...

So here are my doubts: -Is it possible to implement the builder pattern without using static inner classes? -If I have to use the builder pattern by using the inner class, do you think it is correct to keep the getters and the setters? -I did a little example for practice where I tried to avoid the inner class. Could you let me what do you think about it?

Product

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

Builder

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

Client

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


回答1:


The Builder pattern is useful to create immutable objects and avoid several constructors with optional parameters.

IMO using Builder pattern to build a POJO which can be updated using setters is useless. You only create an additional class.

Depending on the ORM framework used, it might not need the presence of setter method. But only assigning members values through reflection.

Product class:

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

Builder class:

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



回答2:


The builder pattern is most useful in the context of immutable objects. Immutable objects don't have setters by definition. So all of their properties have to be squeezed into the constructor. This is where the builder pattern comes in handy. It allows you to split the initialization of a complex immutable object into multiple, self-explaining instructions so you don't need to have constructor calls like this fictional example all over you code where you can't tell which argument does what:

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

I don't find that the Builder pattern creates any signficant value when the created object is mutable. Everything you do through the builder can also be done with the created object directly.

Also, I think that your program above is not a textbook example of the builder pattern. You usually don't create an object by passing the builder to the constructor. You create an object by calling a create method on the builder which then passes all its properties to the constructor of the object. This pattern has the advantage that it gives the builder the opportunity to check its internal state for consistency and maybe throw an exception before starting to build the object.

The java StringBuilder class is a good example (the create method being tostring in this case).




回答3:


What does using a builder here actually gain you?

Nothing as far as I can see: you could just create a new Product and use the getters and setters on it directly. If you have a simple POJO then there is absolutely nothing wrong with:

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

Saving a few characters of typing is IMHO not worth introducing a new class / builder abstraction.

Builders are more useful in situations like the following:

  • You want to create an immutable object (and hence can't use setters)
  • If you have complicated factory logic that cannot easily be expressed with simple getters and setters or that you want to re-use in different ways
  • (Occasionally) when you want to have different parts of the code base configure different aspects of the builder for some reason.



回答4:


The builder pattern lends itself well to producing immutable classes, but it can still be a good option for mutable classes too.

A builder is a reasonable design choice in any situation where an object contains many fields that need to be set during construction; particularly if sensible defaults can be chosen for several of the values.

Whether you use an inner class depends upon your goals. If you wish to force construction through the builder, you can define the builder as an inner class and ensure the outer class only has a private constructor.




回答5:


Here are Builder collaborations from GoF book: Collaborations 1. The client creates the Director object and configures it with the desired Builder object. 2. Director notifies the builder whenever a part of the product should be built. 3. Builder handles requests from the director and adds parts to the product. 3. The client retrieves the product from the builder.

The Builder pattern focuses on constructing a complex object step by step. Builder returns the product as a final step. The returned class in absence of the setters may be as good as immutable. With setters, it can be modified. And inner classes help mask the details.

Another point worth noting is that a major motivation behind creational design patterns is that the client doesn't worry about creating the product. The object creation process is delegated to factory, builder, etc. The client doesn't have to worry about object creation. It will specify what it wants and will get it as a result of delegated creation process.




回答6:


Is it possible to implement the builder pattern without using static inner classes?

Absolutely, yes. As far as the Builder design pattern is concerned, it does not make any difference if the Builder is an inner class or not.

-If I have to use the builder pattern by using the inner class, do you think it is correct to keep the getters and the setters?

Yes, it is ok. it is kind of like, build the object using a certain template and then customize it, if needed.

-I did a little example for practice where I tried to avoid the inner class. Could you let me what do you think about it?

Two problems -

  1. the example does not justify the usage of Builder pattern. Builder pattern is used to build a complex object. So if you can simply build product as : Product p = new Product(); p.setColor(c); p.setPrice(prc); Then there is hardly any benefit in the way you have shown.
  2. Product should not have a dependency on Builder.



回答7:


I found myself thinking if getters are any good in a builder. A builder shouldn't generally be used as a return value - a single method or class should be responsible for creation of the entity using builder. Thus, the method (or class) should keep the information it needs instead of getting it back.

For those reasons, I decided not to use any getters in the builder class. Builder only has setters (be it withAbc(...), setAbc(...) or abc(...)), build() and possibly some private methods like validate().

Using class Product, a sample entity looks like this:

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

Now, a builder class is an inner class of the entity, which allows me to use private constructors.

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

As you can see, I added method builder() to get builder as a copy of the instance and emptyBuilder() as a factory method to hide constructor (maybe there is a better name for it).

Also, when constructing an immutable class, make sure everything inside is immutable as well. Collections are tricky, you have to make a copy, then use Collections.unmodifiable*(...) on it to ensure nobody has a reference to the collection lying under the unmodifiable wrapper.

EDIT: Allegedly, you need getters if you have abstract superclass. That is an overstatement. You only need it if you have a constructor with all params. If you pass the builder instead, like me, you get:

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

So, do we need getters? This time, not really. We're still fine without them. Some other ideas?




回答8:


Builder is about several things and you may want to utilize only one aspect: fluent API. You may attain the best fit for your needs by just changing your setters to return this instead of void. Then you can use the chained-setter idiom: return new MyBean().setCheese(cheese).setBacon(bacon);

On a side note, the term "POJO" does not mean the same as "JavaBean". In fact, sometimes these two terms are used as opposites. The point of a POJO is that it doesn't conform to anything else than being a Java object. It may use public variables, for example.



来源:https://stackoverflow.com/questions/12930852/clearing-doubts-about-the-builder-pattern

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!