Resolving doubts about the builder pattern

I am studying the build pattern, and so far I have realized that this is a great alternative to the patterns commonly used for initialization:

  • Telescope constructor design

  • JavaBean Template

The fact is, I don’t really like to remove getters and setters from the objects of my domain model. I always like to keep them ALWAYS. One of the reasons why I don't like this: If I do not use POJO, then it is not easy to comment on variables when using ORM structures ...

So, here are my doubts: - Is it possible to implement a build template without using static inner classes? -If I need to use the builder pattern using the inner class, do you consider it right to save getters and setters? -I made a small example for practice, where I tried to avoid the inner class. Could you tell me what you think about this?

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

enter image description here

+6
source share
8 answers

The Builder pattern is useful for creating immutable objects and excluding multiple constructors with optional parameters.

IMO using the Builder pattern to create a POJO that can be updated with seters is useless. You create an additional class.

Depending on the ORM structure used, the setter method may not be required. But only the assignment of element 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); } } 
+10
source

The layout template is most useful in the context of immutable objects. Immutable objects by definition do not have setters. Therefore, all their properties must be compressed in the constructor. This is where the creator template is useful. This allows you to split the initialization of a complex immutable object into several self-explanatory instructions, so you do not need to have constructor calls like this fictitious example throughout you where you cannot determine which argument does what:

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

I do not think that the Builder template creates any signficant value when the created object is mutable. Everything that you do through the builder can also be performed directly with the created object.

Also, I think that your program above is not an example of a textbook builder template. Usually you do not create an object by passing constructor to constructor. You create an object by calling the create method in the builder, which then passes all of its properties to the constructor of the object. The advantage of this template is that it gives the builder the opportunity to check its internal state for consistency and, possibly, throw an exception before proceeding with the creation of the object.

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

+4
source

What does the builder actually use here?

Nothing as far as I can tell: you could just create a new product and use getters and setters directly. If you have a simple POJO, then there is nothing wrong with:

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

Saving multiple input characters - this is IMHO not worth introducing a new class / builder abstraction.

Constructors are more useful in the following situations:

  • You want to create an immutable object (and therefore cannot use setters)
  • If you have complex factory logic that cannot be easily expressed with simple getters and setters, or that you want to reuse in different ways.
  • (Sometimes) when you want different parts of the code base to configure different aspects of the creator for some reason.
+2
source

The builder drawing is well suited for creating immutable classes, but it can still be a good option for mutable classes.

The constructor is a reasonable choice of design in any situation when the object contains many fields that must be set during construction; especially if multiple values ​​can be set to reasonable defaults.

If you use an inner class depends on your goals. If you want to force a constructor through a builder, you can define the builder as an inner class and make sure that the outer class has only a private constructor.

+1
source

Here's the Builder collaboration from the GoF book: Collaborations 1. The client creates the Director object and customizes it using the desired Builder object. 2. The director notifies the builder when a part of the product is to be built. 3. Builder processes requests from the director and adds details to the product. 3. The customer retrieves the product from the builder.

The Builder template focuses on building a complex object step by step. Builder returns the product as a last step. A return class in the absence of setters can be as good as immutable. With setters, it can be changed. And inner classes help hide details.

Another point worth noting is that the main motivation for design templates to create is that the client is not worried about creating the product. The process of creating an object is delegated to the factory, builder, etc. The client does not need to worry about creating the object. He will determine what he wants and will receive it as a result of the delegated creation process.

+1
source

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

Absolutely, yes. As for the Builder design pattern, it doesn't make any difference if the Builder is an inner class or not.

-If I need to use the builder pattern using the inner class, do you consider it right to save getters and setters?

Yes, everything is in order. it's kind of like building an object using a specific template and then tweaking it if necessary.

- I made a small example for practice, where I tried to avoid the inner class. Could you tell me what you think about this?

Two problems -

  • the example does not justify the use of the Builder pattern. The Builder pattern is used to create a complex object. Therefore, if you can just create a product like: Product p = new Product(); p.setColor(c); p.setPrice(prc); Product p = new Product(); p.setColor(c); p.setPrice(prc); Then there is hardly any benefit in how you showed.
  • Product should not be dependent on Builder .
+1
source

Builder is a few things, and you can use only one aspect: a free API. You can achieve the best fit for your needs by simply changing the settings to return this instead of void . Then you can use the chain idiom: return new MyBean().setCheese(cheese).setBacon(bacon);

On the other hand, the term POJO does not mean the same as "JavaBean". In fact, sometimes these two terms are used as opposites. The point of POJO is that it does not correspond to everything else than a Java object. For example, it can use public variables.

0
source

I thought that if getters would be good at the builder. The builder should generally not be used as the return value β€” the only way or class should be responsible for creating the object using the builder. Thus, a method (or class) should store the information it needs, and not return it.

For these reasons, I decided not to use any getters in the builder class. Builder only has setters (whether using Abc (...), setAbc (...) or abc (...)), build (), and possibly with some private methods like validate ().

Using the Product class, the sample object is as follows:

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

The builder class is now an internal object class that 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 the builder() method to get the builder as a copy of the instance and emptyBuilder() as the factory method to hide the constructor (maybe there is a better name for it).

Also, when building an immutable class, make sure that everything inside is immutable too. Collections are complex, you need to make a copy, and then use Collections.unmodifiable * (...) on it so that no one references the collection lying under the unmodifiable shell.

EDIT . Presumably you need getters if you have an abstract superclass. This is an exaggeration. You need it only if you have a constructor with all the parameters. 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 are all right with them. Some other ideas?

0
source

Source: https://habr.com/ru/post/927921/


All Articles