Make writing a habit together! This is my first day to participate in the “Gold Digging Day New Plan ยท April More text challenge”, click to see the details of the activity.

Model motivation

Whether in the real world or in software systems, there are always complex objects that have multiple components. For example, the computer is composed of CPU, motherboard, memory, hard disk, graphics card, chassis, display, keyboard, mouse and other components; If you’re building your own computer, you’ll need to do a lot of tedious initialization of member variables and nested objects, often hidden in a largely unreadable constructor with a lot of parameters.

The purpose of the builder model is for most users to get their hands on the assembled computer without having to know the details of how the parts are assembled.

For example, let’s think about creating a House House object.

Start with a simple house (four walls and floors, doors, Windows, and roof), but now you want a house with a pool and other amenities like a yard? You can directly create a super constructor with all possible parameters in the House base class and use it to control the House object.

This might seem like the most obvious solution, but it creates a problem: most arguments are not used in general, which makes the constructor call very cumbersome.

Another reason not to use constructors directly to create complex objects: in complex objects, there may be some restrictions, such as that a complex object cannot be used as a complete product if certain attributes are not assigned; Some attributes must be assigned in a certain order, one attribute may not be assigned before another, and so on.

Another solution is to take the object construction code out of the product class and put it in a separate object called Builder/generator.

Builder mode lets you create complex house objects step by step, without calling all the steps, just those necessary to create a particular object configuration.

When you need to create different forms of House objects for the same steps (such as cabin doors made of wood, castle doors made of stone), then you need to create multiple concrete generators to implement abstract generator classes that implement the same set of creation steps in different ways (polymorphisms).

You can further spare the user the process of calling the steps in the generator class and hand it over to the Director class, which defines the order in which the steps are created, and the generator provides the implementation of those steps. Strictly speaking, the Director class is not required, and users can invoke the creation steps in the generator directly in a specific order.

The Director class completely hides the details of the product construction from the user, who simply calls the Director class, which gets the constructed object from the generator and returns it to the user.

โญ The above describes a complete builder pattern from a realistic scenario!

define

The Builder pattern, also known as the generator pattern, belongs to the creation pattern.

Builder mode lets you create complex objects in steps, allowing you to generate objects of different types and forms using the same creation code; The user does not need to care about the attributes and assembly order contained in the object.

UML class diagrams

The model structure

The Builder mode contains the following characters:

  • BuilderThe generator interface declares product construction steps common to all subclass generators
  • ConcreteBuilderConcrete builders/concrete generators provide different implementations of the construction process and can also build products that do not follow a common interface
  • Product: Final generated product object
  • Director: The director defines the sequence of call construction steps so that specific product configurations can be created and reused

More instances

The sample code

Product.java

public class Product {
    private String partA;
    private String partB;
    private String partC;

    public String getPartA(a) {
        return partA;
    }

    public void setPartA(String partA) {
        this.partA = partA;
    }

    public String getPartB(a) {
        return partB;
    }

    public void setPartB(String partB) {
        this.partB = partB;
    }

    public String getPartC(a) {
        return partC;
    }

    public void setPartC(String partC) {
        this.partC = partC;
    }

    @Override
    public String toString(a) {
        return "Product{" +
                "partA='" + partA + '\' ' +
                ", partB='" + partB + '\' ' +
                ", partC='" + partC + '\' ' +
                '} '; }}Copy the code

Builder.java

public abstract class Builder {
    protected Product product = new Product();

    public abstract void buildPartA(a);

    public abstract void buildPartB(a);

    public abstract void buildPartC(a);

    public Product getResult(a) {
        return this.product; }}Copy the code

ConcreteBuilder1.java

public class ConcreteBuilder1 extends Builder {
    @Override
    public void buildPartA(a) {
        product.setPartA("A1");
    }

    @Override
    public void buildPartB(a) {
        product.setPartB("B1");
    }

    @Override
    public void buildPartC(a) {
        product.setPartC("C1"); }}Copy the code

ConcreteBuilder2.java

public class ConcreteBuilder2 extends Builder {
    @Override
    public void buildPartA(a) {
        product.setPartA("A2");
    }

    @Override
    public void buildPartB(a) {
        product.setPartB("B2");
    }

    @Override
    public void buildPartC(a) {
        product.setPartC("C2"); }}Copy the code

Director.java

This class has two main functions: on the one hand, it isolates the customer from the production process; On the other hand, it is responsible for controlling the production process.

The Director Director programs against the abstract builder. The client only needs to know the type of the specific builder, and then calls the builder’s methods through the Director class to return a complete product object.

public class Director {
    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public void changeBuilder(Builder builder) {
        this.builder = builder;
    }

    public Product make(a) {
        builder.buildPartB();
        builder.buildPartA();
        builder.buildPartC();

        returnbuilder.getResult(); }}Copy the code

Client.java

User classes

public class Client {

    public static void main(String[] args) {
        Director director = new Director(new ConcreteBuilder1());
        // Using director to build a complete product
        Product product1 = director.make();
        System.out.println(product1);

        // ChangeBuilder
        director.changeBuilder(newConcreteBuilder2()); Product product2 = director.make(); System.out.println(product2); }}Copy the code

The advantages and disadvantages

โœ” The cleavage client does not have to know the details of the product’s internal composition, decoupling the product itself from the product creation process so that the same creation process can create different product objects.

โžค Each specific builder is independent and can be replaced or added at any time. Users can get different product objects from different builders. Once the new concrete builder is added, there is no need to modify the original class library code, the system is easy to expand, in line with the “open and close principle”.

โžค You can separate complex construction code from the business logic of your product to fit in the “single responsibility principle.”

โŒ The products created by the builder mode generally have more in common, and their components are similar. If the differences between the products are large, the builder mode is not applicable, so its application scope is limited to some extent.

โŒ Complex changes within the product may result in the need to define many specific builder classes to implement such changes, resulting in large systems.

Applicable scenario

The Builder pattern can be used when:

(1) The attributes of the product objects to be generated depend on each other, and the generation sequence needs to be specified.

(2) When it is necessary to create various forms of products, their manufacturing processes are similar and there are only differences in details.

(3) Isolate the creation and use of complex objects and enable the same creation process to create different products.

(4) The product objects that need to be generated have complex internal structures. These product objects usually contain multiple member attributes. The Builder pattern is used to avoid the occurrence of “overlapping constructors”.

If your constructor has a dozen or so optional arguments, calling it can be very inconvenient; So you need to override the constructor (see below), but these constructors still need to call the main constructor, passing in some default arguments to replace the omitted ones.

Such a complex constructor could only be written in a programming language that supports overloading, such as C# or Java

class Pizza {
    Pizza(int size) { ... }
    Pizza(int size, boolean cheese) { ... }
    Pizza(int size, boolean cheese, boolean pepperoni) { ... }
    // ...
Copy the code

Builder mode lets you generate objects in steps and allows you to use only the necessary steps (Director control). By applying this pattern, you no longer need to cram dozens of arguments into constructors.

“Builder mode” landed

(1) JavaMail: Build a complete mail object step by step

(2) In many game software, the map includes sky, ground, background and other components, and the character includes human body, clothing, equipment and other components. The builder mode can be used to design it, and different types of maps or characters can be created by different specific builders.

(3) Has been widely used in JDK:

  • java.lang.StringBuilder#append()
  • java.lang.StringBuffer#append()
  • java.lang.AppendableAll implementations of

Identification: The generator pattern can be identified by a class, which has a method for building objects and multiple methods for configuring objects. Generator methods typically support method chained calls, such as:

someBuilder.setValueA(1)
        .setValueB(2)
        .create();
Copy the code

Model extension

Simplification of the Builder pattern

(1) Omit the Abstract Builder role: Omit the Abstract Builder if the system has only one concrete Builder.

(2) omit the Director role: in cases where there is only one ConcreteBuilder, if the abstract builder role has already been omitted, omit the Director role and make the ConcreteBuilder play both the Director and builder role.

“Builder Mode” versus “Factory Mode”

From the following different dimensions:

The Builder pattern has different concerns than the factory pattern, but the two can also be used together

  • The Builder pattern is used to create complex objects; The factory pattern is used to create simple objects.
  • The Builder model focuses on the assembly process of parts and returns a complete product. Factory mode focuses on the creation of parts and returns parts. Think of the factory model as an auto parts manufacturing plant and the builder model as an auto assembly plant.
  • In factory mode, the client instantiates the chemical class and then calls the factory method to get the desired object. In the Builder mode, the client can not directly call the builder related methods, but through the conductor to guide how to generate objects, it is more focused on building a complex object step by step, return a complete object.

The last

๐Ÿ‘† previous: “Design Patterns” ๐Ÿ›• Abstract Factory

๐Ÿ‘‡ Next article: “Design pattern” ๐ŸŒ“ Prototype pattern (Prototype)

โค๏ธ Good code without explanation, pay attention to the “rip design patterns” column, with me to learn design patterns, your code can be as elegant as poetry!

โค๏ธ / END/If this article is helpful to you, click a “like” to support it, your support is my biggest motivation!