Builder mode practice
Http://www.javacodegeeks.com/2013/01/the-builder-pattern-in-practice.html
Reprinted please indicate the source: http://blog.csdn.net/kingviker/article/details/27058051
I am not planning to jump into too many details of the design model, because I have already explained a lot of articles and books in detail. So I want to tell you why and when you should consider using the design pattern. However, it is worth mentioning that the patterns and design patterns mentioned in this article can be reused. Because the native mode focuses on the abstract building steps, we can get different results by using different builder modes, however, the design pattern explained in this article is to discuss how to remove unnecessary and complex things from the Multi-constructor, multi-optional parameters, and misuse of the setters method.
Suppose you have a class that contains a large number of attributes, just like the following User class. Let's assume that you want to make this class immutable (unless you have a real good reason, by the way, you don't have to always struggle toward the immutable goal. However, we will discuss it in another article ).
public class User { private final String firstName; //required private final String lastName; //required private final int age; //optional private final String phone; //optional private final String address; //optional ...}
Now imagine that some attributes in your class are required and some are optional. How do you create instances of this class? All attributes are declared as final type, so you must set them in the constructor, but you also want the client of this class to have the opportunity to ignore the optional attributes.
One of the first possible options is to have a constructor that only receives required attributes as parameters. One is to receive all required attributes and the first optional attributes, and the other is to receive two optional attributes. What does this look like?
It looks like this:
public User(String firstName, String lastName) { this(firstName, lastName, 0);}public User(String firstName, String lastName, int age) { this(firstName, lastName, age, '');}public User(String firstName, String lastName, int age, String phone) { this(firstName, lastName, age, phone, '');}public User(String firstName, String lastName, int age, String phone, String address) { this.firstName = firstName; this.lastName = lastName; this.age = age; this.phone = phone; this.address = address;}
The advantage of constructing an object is that it works normally. However, the problem with this method is also obvious. When you only have a few attributes, it is not a big problem. However, as the number of attributes increases, the Code becomes increasingly difficult to read and maintain. More importantly, the Code becomes increasingly difficult to use for clients. Which constructor should I call in the client? Which of the following are two parameters? Which of the three parameters? What are the default values of parameters that I do not display for passing values? What should I do if I only want to set a value for the address attribute but do not want to set a value for age and phone? In this case, I have to call the constructor that can receive all the parameters and pass the default value to the optional parameters that I don't care about. In addition, some parameters of the same type are easy to confuse. Is the first String parameter corresponding to phone or address?
So what other options do we have to deal with this scenario? We can always follow the JavaBeans Convention. There is a default construction method without parameters and each attribute has the getter and setter methods. Like this:
public class User { private String firstName; // required private String lastName; // required private int age; // optional private String phone; // optional private String address; //optionalpublic String getFirstName() { return firstName;}public void setFirstName(String firstName) { this.firstName = firstName;}public String getLastName() { return lastName;}public void setLastName(String lastName) { this.lastName = lastName;}public int getAge() { return age;}public void setAge(int age) { this.age = age;}public String getPhone() { return phone;}public void setPhone(String phone) { this.phone = phone;}public String getAddress() { return address;}public void setAddress(String address) { this.address = address;}}
This method seems easy to read and maintain. On the client side, I can create only one empty object and set only the attributes that interest me. So, what is the problem with this method? This solution has two major problems. The first problem is that the instance status of this class is not fixed. If you want to create a User object and assign values to all the five attributes of the object, the object does not have a complete State until all the setXX methods are called. This means that when the object status is incomplete, some client programs may see this object and think that the object has been constructed. The second disadvantage of this method is that the User class is variable. You will lose all the advantages of immutable objects.
Fortunately, we have a third choice for this scenario, the builder mode. The solution is similar to the following:
public class User { private final String firstName; // required private final String lastName; // required private final int age; // optional private final String phone; // optional private final String address; // optionalprivate User(UserBuilder builder) { this.firstName = builder.firstName; this.lastName = builder.lastName; this.age = builder.age; this.phone = builder.phone; this.address = builder.address;}public String getFirstName() { return firstName;}public String getLastName() { return lastName;}public int getAge() { return age;}public String getPhone() { return phone;}public String getAddress() { return address;}public static class UserBuilder { private final String firstName; private final String lastName; private int age; private String phone; private String address; public UserBuilder(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public UserBuilder age(int age) { this.age = age; return this; } public UserBuilder phone(String phone) { this.phone = phone; return this; } public UserBuilder address(String address) { this.address = address; return this; } public User build() { return new User(this); }}}
Important points:
- The User constructor is private, which means that the class cannot be directly instantiated in the client code.
- This class is now immutable. All attributes are of the final type and are assigned values in the constructor. In addition, we only provide the getter Method for them.
- The builder class uses the stream interface style to make the client code easier to read (we will immediately see an example of it ).
- The builder class constructor only accepts required attributes. To ensure that these attributes are assigned values in the constructor, only these attributes are defined as final types.
Using the builder pattern has all the advantages of the two methods mentioned at the beginning of this article, without their disadvantages. The client code is simpler to write, and more importantly, easier to read. The only criticism I have heard about this pattern is that you must copy the attributes of the class in the builder class. However, considering this fact, builder classes are usually static Class Members of the classes to be built, and they are easily extended together.
What does the client code that tries to create a new User object look like? Let's take a look:
public User getUser() { return new User.UserBuilder('Jhon', 'Doe') .age(30) .phone('1234567') .address('Fake address 1234') .build();}
Very clean, isn't it? You can create a User object with only one line of code. More importantly, the code is easy to read. In addition, we also make sure that whenever you get an object of this class, the object will not be in an incomplete state.
This mode is very flexible. A separate builder class can create multiple objects by changing the builder attributes before calling the build method. The builder class can even automatically complete some generated fields, such as an id or serial number, between each call.
It is very important that, for example, the constructor method, the builder class can add constraints on the constructor parameters. The build method can check these constraints. If not, an IllegalStateException is thrown.
It is vital to verify the parameter after the builder parameter is copied to the build object. In this way, the field of the build object is verified, not the builder field. The reason for this is that the builder class is not thread-safe. If we verify the parameter before creating a real object, the parameter value may be modified by another thread between parameter verification and parameter copying. This period is called a "Window of weakness ".
In our User example, similar code is as follows:
public User build() { User user = new user(this); if (user.getAge() 120) { throw new IllegalStateException(“Age out of range”); // thread-safe } return user;}
The previous code version is thread-safe because we first create a user object and then verify the condition constraints on the immutable object. The following code looks the same in terms of functionality, but it is not thread-safe. You should avoid doing so:
public User build() { if (age 120) { throw new IllegalStateException(“Age out of range”); // bad, not thread-safe } // This is the window of opportunity for a second thread to modify the value of age return new User(this);}
The last advantage of the builder mode is that the builder can pass it as a parameter to a method, so that the method can create one or more objects for the client without knowing any details about the object creation. To do this, you may need a simple interface as follows:
public interface Builder { T build();}
Using the previous User example, the UserBuilder class can implement Builder. In this way, we can have the following code:
UserCollection buildUserCollection(Builder
userBuilder){...}
Well, this is indeed a long article, although it was the first time. All in all, the builder mode has more than a few parameters (although not very scientific and accurate, I usually use four parameters as a good indicator to use the builder mode ), especially when most parameters are optional. You can make the client code easier to read, write, and maintain. In addition, your class can maintain the immutable feature, making your code safer.
UPDATE: If you use eclipse as the IDE, you have a considerable number of plug-ins to avoid code duplication in most of the builder mode. I know three of the following: