Builder Pattern In Practice)

Source: Internet
Author: User

Builder Pattern In Practice)

 

I will not describe this mode in detail, because a large number of articles or books have already analyzed this mode in detail. I will tell you why and when you should consider using it. It is worth mentioning that there are some differences between this model and the four-person book of design patterns (design patterns: the basis for reusable object-oriented software. The generator mode introduced in the help of four people mainly abstracts the steps for object creation, and calls different implementations to get different results, the purpose of the generator mode described in this article is to remove unnecessary complexity introduced by excessive use of optional parameters and setters due to multiple overloaded constructors.

If your code defines a User class with a large number of attributes similar to the following, suppose you want to define the class as immutable (by the way, unless there is enough reason, otherwise, you should try to define variables as immutable. We will talk about them 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, assuming that some attributes of the User class are mandatory and some attributes are optional, how do you create such a class instance? Since all attributes are declared as final, you need to assign values to them in the constructor, but you want users of this class to ignore optional parameters during instantiation.

 

The simplest and most effective method is to define a constructor that only receives required parameters, a constructor that receives all required parameters and an optional parameter, A constructor that receives all required parameters and two optional parameters, and so on. What does this code look like? It is roughly as follows:

 

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 good news is that it is feasible to create a class instance. However, this method has obvious problems. This method does not affect a class with only a few attributes. However, as the number of class attributes increases, the Code becomes increasingly difficult to read and maintain. More seriously, the Code becomes more difficult for callers. Which constructor should I use as the caller? Is it a constructor with two or three parameters? What is the default value of an optional parameter if I do not display it? What if I only need to set the address and do not want to set the age and mobile number? In this case, I need to call the constructor with the specified parameters and pass a default value to those parameters that I am not interested in. In addition, when several parameters have the same type, it is easy to confuse the caller's use. Does the first String type parameter refer to the mobile phone number or address?

 

Do we have other options for the above situations? We can follow the JavaBeans specification to define a default no-argument constructor and provide the setters and getters functions for each attribute, as shown below:

 

public class User {private String firstName; // requiredprivate String lastName; // requiredprivate int age; // optionalprivate String phone; // optionalprivate 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;}}

The above method seems easy to read and maintain. As a user, I can create an empty instance and set only the attribute values that interest me. What's wrong? This solution has two major problems. The first problem is that the instances of this class are not consecutive. If you want to create a class instance with five attribute values at the same time, the instance will not be in the complete State until the setX function of all attribute values is called. This means that some modules of the caller application may see the incomplete status of this instance. The second disadvantage is that this solution makes the User class variable, and you will lose the benefits of many immutable objects.

 

Fortunately, we have a third option: generator mode. This solution is similar to the following code:

 

public class User {private final String firstName; // requiredprivate final String lastName; // requiredprivate final int age; // optionalprivate final String phone; // optionalprivate 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);}}}

 

There are several key points to note: 1) The User class constructor is private, which means that the caller cannot directly instantiate this class; 2) This class becomes immutable again, all required attribute values are final and set in the constructor. In addition, we only provide the getters function for attribute values; 3) This mode uses Fluent Interface idioms (see the http://martinfowler.com/bliki/FluentInterface.html) to make the caller's code more readable (we will see this example later); 4) the constructor in this mode only receives the required attribute values as parameters, and only these required attribute values are set to final to ensure that they are set in the constructor; the generator mode has the advantages of the other two schemes described above, without their disadvantages. The caller's code is easier to write, and more importantly, it is easier to read. The only criticism I have heard of this pattern is that you must repeat the attribute definitions of external classes in the internal class of the builder. However, since builder classes are usually static internal classes of the classes they build, they can be easily modified synchronously. What does the caller code try to create a User instance? As follows:
public User getUser() {return newUser.UserBuilder('Jhon', 'Doe').age(30).phone('1234567').address('Fake address 1234').build();}
It's quite concise, isn't it? You can use a line of code to create a User instance. More importantly, the code is easy to read. In addition, you can ensure that the User instance obtained at any time is in the complete state. This mode is quite flexible. A builder can create different class instances by setting different attribute values before calling the build function. Builder can even automatically complete the generated fields, such as the id value or serial number, between each call. It is important that builder can make its parameters immutable, similar to constructor. The build function can check these Immutable parameters and throw an IllegalStateException when the parameters are invalid.

 

Parameters are copied from the builder class to the external class, and the validity is verified in the external class instead of the builder class. This is crucial. The reason is that the builder class is not thread-safe. If we check the parameter validity before creating an external class object, the parameter checksum and parameter are copied to the time range of the external class, the value of these parameters may be changed by another thread. This time period is the famous "window of vulnerability", a time of weakness, especially during the Cold War, the U. S. land-based missiles can easily become the first target of Soviet Union attacks ). In our User example, the code is similar to the following:

public User build() {    User user = new user(this);    if (user.getAge() < 120) {        throw new IllegalStateException(“Age out of range”); // thread-safe    }    return user;}

The above version is thread-safe, because we first create a User instance, and then verify the non-variable in the User immutable instance. The following code seems to implement the same functions, but it is not thread-safe. Therefore, we should avoid using this method:

 

 

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 benefit of this mode is that the builder can pass it to another function, so that the function can create one or more object instances for the caller without knowing the specific creation details of the object instance. To achieve this goal, we usually define a simple interface as follows:

 

 

public interface Builder
 
   {    T build();}
 
In the previous User example, the UserBuilder class needs to be changed to implement Builder. In this way, the build function is similar to the following:

 

 

UserCollection buildUserCollection(Builder
   userBuilder){...}

Well, this is my first long blog. To sum up, the generator mode has more than a few parameters (not accurate, usually I will use this mode for classes with four or more attributes.) The construction of classes is a good choice, especially when most of these attributes are optional. The application mode makes the caller code easy to read, write, and maintain. In addition, because your class is unchangeable, your code will be more secure.

 

 

Update: If you use Eclipse as your IDE, you can use many plug-ins to simplify the compilation of the Sample Code introduced in this mode. The three plug-ins I know are as follows: 1www.bkjia.com I have never tried these plug-ins, so I cannot conclude which one is better. I think other ides also have similar plug-ins.



 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.