Builder pattern is a common design pattern in daily development. Its main function is to abstract out the process of creating complex things. Different implementation methods of this abstraction are different, and the objects created are also different. Generally speaking, there is a fixed step to create an object. This fixed step is abstracted out. Each abstraction step has a different implementation method, and different implementation methods create different objects. Take a common example, we must have bought a computer, computer production or assembly actually belongs to the builder mode, we know, computer production needs to install CPU, memory, hard disk and other components. This installation step can be abstracted, and which CPU to install, such as I5 or i7, is a concrete implementation of this abstract installation step.

Builder mode?

The Builder Pattern uses multiple simple objects to build a complex object step by step.

Builder mode mainly solves the problem of creating “a complex object” in software system, which is usually composed of sub-objects of each part with certain algorithm. The pieces of this complex object often face drastic changes due to changing requirements, but the algorithms that put them together are relatively stable.

Builder pattern

There are two types of Builder mode, one for classic builder mode and the other for variant Builder mode.

Classic Builder Mode

As you can see from the image above, there are four characters in classic Buider mode:

  • Builder: Gives an abstract interface to regulate the construction of the components of a product object. This interface specifies which parts of a complex object are to be created, and does not involve the creation of specific object parts.
  • ConcreteBuilder: Implements the Builder interface that concretes the creation of parts of complex objects for different business logic. Provide examples of products after the construction process is complete.
  • Director: Calls concrete builders to create parts of a complex object. There is no product-specific information involved in the mentor, only ensuring that parts of the object are created intact or in some order.
  • Product: Complex object to be created.

1. Create a Product object:

public class Computer {
    /*CPU*/
    private String CPU;
    / * * / memory
    private String memory;
    / * * / harddisk
    private String hardDisk;
    / * * / keyboard
    private String keyboard;
    / * * / mouse
    private String mouse;

    public String getCPU(a) {
        return CPU;
    }

    public void setCPU(String CPU) {
        this.CPU = CPU;
    }

    public String getMemory(a) {
        return memory;
    }

    public void setMemory(String memory) {
        this.memory = memory;
    }

    public String getHardDisk(a) {
        return hardDisk;
    }

    public void setHardDisk(String hardDisk) {
        this.hardDisk = hardDisk;
    }

    public String getKeyboard(a) {
        return keyboard;
    }

    public void setKeyboard(String keyboard) {
        this.keyboard = keyboard;
    }

    public String getMouse(a) {
        return mouse;
    }

    public void setMouse(String mouse) {
        this.mouse = mouse;
    }

    @Override
    public String toString(a) {
        return "Computer{" +
                "CPU='" + CPU + '\' ' +
                ", memory='" + memory + '\' ' +
                ", hardDisk='" + hardDisk + '\' ' +
                ", keyboard='" + keyboard + '\' ' +
                ", mouse='" + mouse + '\' ' +
                '} '; }}Copy the code

2. Create an abstract computer assembly process Builder class:

public interface IComputerConfigBuilder {
    /** * CPU */
    void setCpu(a);

    /** * memory */
    void setMemory(a);

    /** * disk */
    void setHardDisk(a);

    /** * keyboard */
    void setKeyboard(a);

    /** * mouse */
    void setMouse(a);

    /** * computer **@return* /
    Computer getComputer(a);
}
Copy the code

ComputerConfigBuilder (ComputerConfigBuilder) provides a class that implements the installation of a CPU, memory, hard disk, keyboard and mouse, and a method that obtains a Conputer.

3. Now that we have an abstract assembly process, we need to create concrete implementation classes.

We know that computers generally have low and high matching version, different configuration, the assembly of the computer is not the same. Create a package LowConfigBuilder that implements ComputerConfigBuilder.

// I5 CPU, 8GB ram, 500GB hard disk, thin film keyboard and wired mouse.
public class LowConfigBuilder implements IComputerConfigBuilder {

    private Computer mComputer;

    public LowConfigBuilder(a) {
        this.mComputer = new Computer();
    }

    @Override
    public void setCpu(a) {
        mComputer.setCPU("i5");
    }

    @Override
    public void setMemory(a) {
        mComputer.setMemory("8G");
    }

    @Override
    public void setHardDisk(a) {
        mComputer.setHardDisk("500G");
    }

    @Override
    public void setKeyboard(a) {
        mComputer.setKeyboard("Membrane keyboard");
    }

    @Override
    public void setMouse(a) {
        mComputer.setMouse("Wired mouse");
    }

    @Override
    public Computer getComputer(a) {
        returnmComputer; }}Copy the code

Then we create a premium package:

// The I7's CPU, 16GB of ram, 1TB of hard disk, mechanical keyboard and wireless mouse
public class HighConfigBuilder implements IComputerConfigBuilder {

    private Computer mComputer;

    public HighConfigBuilder(a) {
        this.mComputer = new Computer();
    }

    @Override
    public void setCpu(a) {
        mComputer.setCPU("i7");
    }

    @Override
    public void setMemory(a) {
        mComputer.setMemory("16G");
    }

    @Override
    public void setHardDisk(a) {
        mComputer.setHardDisk("1T");
    }

    @Override
    public void setKeyboard(a) {
        mComputer.setKeyboard("Mechanical keyboard");
    }

    @Override
    public void setMouse(a) {
        mComputer.setMouse("Wireless mouse");
    }

    @Override
    public Computer getComputer(a) {
        returnmComputer; }}Copy the code

4.Director is the core of the builder mode, which calls specific builders to create unconfigured computers:

public class Director {
    private IComputerConfigBuilder mBuilder;

    /** * setBuilder to tell you what to configure the computer **@param builder
     */
    public void setBuilder(IComputerConfigBuilder builder) {
        this.mBuilder = builder;
    }

    /** * Step by step to assemble the computer */
    public void createComputer(a) {
        mBuilder.setCpu();
        mBuilder.setMemory();
        mBuilder.setHardDisk();
        mBuilder.setKeyboard();
        mBuilder.setMouse();
    }

    /** * Get the assembled computer **@return* /
    public Computer getComputer(a) {
        returnmBuilder.getComputer(); }}Copy the code

We need to use setBuilder to tell him what configuration the computer needs. Then we can use createComputer to assemble the computer step by step. After assembling the computer, we can call getComputer method to get the computer we need.

/ / test
    @Test
    public void buildPatternsV1Test(a) {
        Director director = new Director();// Create an installer
        director.setBuilder(new LowConfigBuilder()); // Tell the installer about the computer configuration
        director.createComputer(); // The installation staff starts the assembly
        Computer computer = director.getComputer(); // Get the assembled computer from the installer
        System.out.print("Computer Configuration:" + computer.toString());  // Check the computer configuration

// director.setBuilder(new HighConfigBuilder());
// director.createComputer();
// Computer computer = director.getComputer();
// system.out. print(" computer.tostring () ");
    }
Copy the code

Mutant Builder mode

Today, the Boss suddenly comes and throws a requirement to you: You need to create an immutable Person object, which can have the following attributes: name, gender, age, occupation, car, shoes, clothes, money, house. A name and gender are mandatory.

public class Person {
    /* Name (required) */
    private final String name;
    /* Gender (required) */
    private final String gender;
    /* Age (optional) */
    private final String age;
    /* Shoes (optional) */
    private final String shoes;
    /* Clothing (optional) */
    private final String clothes;
    /* Money (not required) */
    private final String money;
    /* House (optional) */
    private final String house;
    /* Car (optional) */
    private final String car;
    /* Profession (optional) */
    private final String career;

    public Person(String name,String gender,String age,String shoes,String clothes,String money,String house,String car,String career){
        this.name = name;
        this.gender = gender;
        this.age = age;
        this.shoes = shoes;
        this.clothes = clothes;
        this.money = money;
        this.house = house;
        this.car = car;
        this.career = career;
    }

    public Person(String name, String gender){
        this(name,gender,null.null.null.null.null.null.null); }}Copy the code

Since the Person object you are creating is immutable, you declare all the attributes in the class final and define a constructor with all the attributes as parameters. Since name and gender are required, you define a separate constructor with name and gender as parameters for the convenience of the caller. There you have it, and you submit it to the Boss, and he looks at it and he’s happy with it, but after a while, he tells you that if you need to pass in attributes that aren’t necessary, this constructor isn’t very easy to call because it has too many arguments, It’s easy to mistransmit. You can’t put all the parameters in the constructor. Soon you come up with the idea of using the set method:

public class Person {
    /* Name (required) */
    private String name;
    /* Gender (required) */
    private String gender;
    /* Age (optional) */
    private String age;
    /* Shoes (optional) */
    private String shoes;
    /* Clothing (optional) */
    private String clothes;
    /* Money (not required) */
    private String money;
    /* House (optional) */
    private String house;
    /* Car (optional) */
    private String car;
    /* Profession (optional) */
    private String career;

    public String getName(a) {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender(a) {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getAge(a) {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getShoes(a) {
        return shoes;
    }

    public void setShoes(String shoes) {
        this.shoes = shoes; }... }Copy the code

To create an object, just do the following:

        Person person = new Person();
        person.setName("Zhang");
        person.setAge("22");
        person.setGender("Male");
        person.setCareer("Programmer"); .Copy the code

It looks more clear, as long as to create an object, what want to assign a value set up is ok, but you see, or find a lot of problems, first of all, use the set method, violates the demand, this object is immutable, for the first and second with this method a set a value assignment, pretend bility is not high, The other thing is you’re probably going to end up with an incomplete Person object, because when you’re done with the Person object, you might forget the set for whatever reason, and you’re not going to get the Person object that you expected. At this point, you get a little confused, so you do a Google search, and you actually find a solution, using this variant of Builder mode:

public class Person {
    /* Name (required) */
    private final String name;
    /* Gender (required) */
    private final String gender;
    /* Age (optional) */
    private final String age;
    /* Shoes (optional) */
    private final String shoes;
    /* Clothing (optional) */
    private final String clothes;
    /* Money (not required) */
    private final String money;
    /* House (optional) */
    private final String house;
    /* Car (optional) */
    private final String car;
    /* Profession (optional) */
    private final String career;

    private Person(Builder builder) {
        this.name = builder.name;
        this.gender = builder.gender;
        this.age = builder.age;
        this.shoes = builder.shoes;
        this.clothes = builder.clothes;
        this.money = builder.money;
        this.house = builder.house;
        this.car = builder.car;
        this.career = builder.career;
    }

    public static class Builder {
        private final String name;
        private final String gender;
        private String age;
        private String shoes;
        private String clothes;
        private String money;
        private String house;
        private String car;
        private String career;

        public Builder(String name,String gender) {
            this.name = name;
            this.gender = gender;
        }

        public Builder age(String age) {
            this.age = age;
            return this;
        }

        public Builder car(String car) {
            this.car = car;
            return this;
        }

        public Builder shoes(String shoes) {
            this.shoes = shoes;
            return this;
        }

        public Builder clothes(String clothes) {
            this.clothes = clothes;
            return this;
        }

        public Builder money(String money) {
            this.money = money;
            return this;
        }

        public Builder house(String house) {
            this.house = house;
            return this;
        }

        public Builder career(String career) {
            this.career = career;
            return this;
        }

        public Person build(a){
            return new Person(this); }}Copy the code

Since the Person object is immutable, we make sure that all of its attributes are final, but we don’t have to do that if there is no immutable requirement. Then we define an inner class Builder in the Person class. The properties in the inner class Builder must be the same as those in the Person class. Properties that must be modified with final, in case they are not assigned, and properties that are not required cannot be modified with final, because if final is added, they must be initialized, so that those properties that are not required become required again. A constructor is then defined in the inner class, passing in the required attributes. Other non-required properties are set by methods, each of which returns the Builder object itself. Finally, we define a build method that passes the Builder object to the private constructor of Person and returns an object.

Let’s look at creating a Person:

        Person person = new Person.Builder("Zhang"."Male")
                .age("12")
                .money("1000000")
                .car("BMW")
                .build();
Copy the code

Doesn’t it look like forcing the lattice to instantly improve, non-essential properties can be set according to the need for arbitrary, very flexible, and so set the first property and then create the object, the final object must be the complete object you expect, unlike the previous set method to create the object may not have been set completely. Well, when you’re done, you can’t wait to submit the Person class to the Boss, and sure enough, the Boss is happy with the way the object is created.

It uses the inner class approach, making its own class methods private and then providing a static inner class to create objects. This class also provides a default value for the object by chaining the call by returning this

Precautions for using Builder mode

  1. Without knowing the details of the product’s internal composition, the client decouples the product itself from the product creation process so that the same creation process can create different product objects
  2. Each concrete builder is relatively independent of other concrete builders, so it is easy to replace concrete builders or add new concrete builders, and users can use different concrete builders to get different product objects
  3. The product creation process can be more finely controlled. Breaking down the creation steps of complex products into different methods makes the creation process clearer and easier to control programmatically
  4. Adding a new concrete builder does not need to modify the code of the original class library. The commander class is programmed for the abstract builder class, and the system is easy to expand and conforms to the “open and close principle”.
  5. The products created by the Builder mode generally have more in common and their components are similar. If there is a large difference between the products, the builder mode is not suitable for use, so its application scope is limited to a certain extent.
  6. If the internal changes of the product are complex, it may result in the need to define many specific builder classes to implement the changes, resulting in a large system, so consider whether to choose the builder pattern in this case.
  7. Abstract Factory pattern vs. Builder Pattern Abstract factory pattern implements the creation of a family of products, a family of products is a series of products; For product portfolios with different classification dimensions, the abstract factory model does not need to care about the construction process, but only what products are produced by what factories. The builder mode requires that the product be built according to the specified blueprint. Its main purpose is to create a new product by assembling parts.

The Builder mode is similar to the Factory mode in that they are both builder modes and apply similar scenarios. In general, if the product is complex to build, use the factory model; If the product is more complex to build, use the builder pattern.

Refer to the article

  • www.jianshu.com/p/afe090b2e… (Mostly from this article)
  • zhuanlan.zhihu.com/p/58093669
  • Cloud.tencent.com/developer/a…
  • www.manongjc.com/article/120…