Builder
1. Several ways to build an object
- 1. Multiple constructors: Although this method is the most convenient, it is also the most cumbersome. Suppose there is a method that needs to set six int type parameters, but in most cases, there are always parameters you don't want to pass. At this time, you need to agree on a rule: for example, pass 0, but there will be a problem in passing 0, that is, the pass in sequence. Different pass in sequences will not cause the program compilation to fail, but will cause the program to run incorrectly. So this is not a good way
- 2. setter: although it can solve the problem of incoming order caused by multiple constructs, it is still a little cumbersome. So we usually only use setters in JavaBean s
- 3. Builder mode: a mode to be introduced in this article can perfectly solve the disadvantages brought by the above two modes and make the code more in line with the design mode. Refer to the usage of Glide and Retrofit, which are connected one by one through chain calls
Multi constructor example
public class UserConstruct { /** * id */ private Long id; /** * full name */ private String name; /** * ID number */ private String idNumber; /** * address */ private String address; /** * Whether the information is complete * true : name,idNumber,address Must be * false : One or more of the above attributes are empty */ private boolean infoIsComplete; public UserConstruct(Long id, String name) { this(id, name, null, null); } public UserConstruct(Long id, String name, String idNumber) { this(id, name, idNumber, null); } public UserConstruct(Long id, String name, String idNumber, String address) { this.id = id; this.name = name; this.idNumber = idNumber; this.address = address; if (null != id && null != name && null != idNumber && null != address) { this.infoIsComplete = true; } } }
Userconstruct The Java class has five member variables, among which ID, name and infoIsComplete are required member variables, and infoIsComplete is not set by the user, but is assigned according to the actual attribute setting of the object. IDnumber and address are two optional variables.
When we need to create an object with an address but no ID number, we will write this:
UserConstruct user = new UserConstruct(1L, "Zhang San", null, "Shanghai");
But constructor calls usually require many parameters that you don't want to set at all, but you have to pass values for them.
The overlapping constructor pattern works, but when there are many parameters, the client code is difficult to write and still hard to read.
When there are a series of parameters of the same type, it is easy to reverse the order of some of them, but the compiler will not make an error.
So you usually think of using JavaBean pattern to replace it.
Set: JavaBean mode
In this mode, we usually call a parameterless constructor to build objects, and then call the setter method to set each necessary parameter and each related optional parameter. Our user class becomes like this:
public class UserJavaBean { /** * id */ private Long id; /** * full name */ private String name; /** * ID number */ private String idNumber; /** * address */ private String address; /** * Whether the information is complete * true : name,idNumber,address Must be * false : One or more of the above attributes are empty */ private boolean infoIsComplete; public void setId(Long id) { this.id = id; } public void setName(String name) { this.name = name; } public void setIdNumber(String idNumber) { this.idNumber = idNumber; } public void setAddress(String address) { this.address = address; } public void setInfoIsComplete(boolean infoIsComplete) { this.infoIsComplete = infoIsComplete; } }
In the JavaBean mode, the example in initializing an overlapping constructor becomes as follows:
UserJavaBean user = new UserJavaBean(); user.setId(1L); user.setName("Zhang San"); user.setAddress("Shanghai"); user.setInfoIsComplete(false);
This makes the code more readable, but it also has several disadvantages:
- Required parameters cannot be guaranteed
In this way, we can not guarantee that all the necessary parameters are assigned values as we wish. Of course, there is a remedy for this shortcoming, that is, we call a constructor containing all the necessary parameters to obtain an object, and only set the relevant optional parameters through the setter method.
- JavaBean s may be in inconsistent states during construction
The description in the book is a bit obscure and difficult to understand. I don't know whether it is a translation problem. An example is that the infoIsComplete field must be set to true only after all attributes have been assigned values. In this way, we do not have a good way to automatically assign values to them through code through JavaBean s.
Another point is that if the idNumber and address two optional parameters must exist at the same time, using JavaBean s is a little too weak.
- The JavaBean pattern prevents the possibility of making classes immutable
Once we use the JavaBean pattern, it means that we will write a common setter method for each attribute of the class, which means that our class cannot become an immutable class.
2. Define and use scenarios
The definition of Builder is to separate the construction of a complex object from its representation, so that the same construction process can create different representations
Usage scenarios
It mainly emphasizes two points: calling sequence and multi parameter
-
1. When the same method and different execution sequence need to produce different event results
-
2. When multiple components or parts can be assembled into an object, but the running results are different
-
3. When the product class is very complex, or the calling sequence in the product class has different effects
-
4. When initializing an object is particularly complex, for example, there are many parameters, and many parameters have default values
3. Advantages and disadvantages
The builder has the following advantages:
- You can impose constraints on parameters just like constructors, such as idNumber and address must exist at the same time.
- Can have multiple variable parameters
- It is very flexible. You can build multiple objects through a single Builder, and you can also adjust parameters when building objects.
Of course, the builder also has some shortcomings:
- In order to create an object, we must first create its builder object, which may consume our memory to some extent. Therefore, in some performance-oriented situations, the builder is not so easy to use.
However, in most cases, it is recommended that we use the builder in classes with a large number of current or future parameters.
3. Use
According to the description in the book, the builder can not only ensure the security like the overlapping constructor, but also have the readability like the JavaBean pattern.
public class UserBuilder { /** * id */ private Long id; /** * full name */ private String name; /** * ID number */ private String idNumber; /** * address */ private String address; /** * Whether the information is complete * true : name,idNumber,address Must be * false : One or more of the above attributes are empty */ private boolean infoIsComplete; /** * Builder */ public static class Builder { // Mandatory parameters private Long id; private String name; // Optional parameters private String idNumber; private String address; public Builder(Long id, String name) { this.id = id; this.name = name; } public Builder idNumber(String idNumber) { this.idNumber = idNumber; return this; } public Builder address(String address) { this.address = address; return this; } public UserBuilder build() { return new UserBuilder(this); } } private UserBuilder(Builder builder) { this.id = builder.id; this.name = builder.name; this.idNumber = builder.idNumber; this.address = builder.address; // Assign a value to infoIsComplete according to the parameter assignment if (this.id != null && this.name!= null && this.idNumber!= null && this.address != null) { this.infoIsComplete = true; } } }
The above example can be rewritten as:
UserBuilder user = new Builder(1L, "Zhang San").address("Shanghai").build();
It is also easy to read, and we can also verify the parameters in the constructor of UserBuilder, and we can smoothly assign values to the infoIsComplete attribute automatically.
reference resources
1,Article 2 of Effective Java: when multiple constructor parameters are encountered, the constructor should be considered
2,Book record of Effective java - Article 2 - consider using constructors when multiple constructor parameters are encountered
3,[reading notes] Article 2 in Chapter 2 of Effective Java: consider using Builder when multiple constructor parameters are encountered