Design pattern - builder pattern

Posted by seeker2921 on Sun, 26 Dec 2021 07:00:01 +0100

background

When the internal data of a class is too complex (it is usually a class that is responsible for holding data, such as Config, VO, PO, Entity...). To create it, you may need to understand the internal structure of this class and how these things are organized and assembled. At this time, the learning cost will be increased and it will be very chaotic. At this time, think about a way to manage the data in this class As far as I know, how to make it step by step when creating, and the code readability is very good. Don't let me be dazzled. Everything I want can be well set up. This is the application scenario of Builder mode, which can separate the construction and representation of a class.

1. Introduction

1.1 what is the builder model

The creator pattern, also known as the builder pattern, separates the construction of a complex object from its representation
The same build process can create different representations. Creator mode hides the creation process of complex objects. It abstracts the creation process of complex objects and dynamically creates objects with composite properties by subclass inheritance or overloading.

1.2 applicable scenarios:

  • Isolate the creation and use of complex objects. The same method and different execution sequences produce different event results
  • Multiple assemblies can be assembled into one object, but the running results are different
  • Product classes are very complex or have different functions due to different call sequences
  • When initializing an object, there are too many parameters, or many parameters have default values
  • The Builder pattern is not suitable for creating product classes with great differences
    The internal changes of the product are complex, which will lead to the need to define many specific builder classes to realize changes, increase the number of classes in the project, and increase the understanding difficulty and operation cost of the system
  • The product objects to be generated have complex internal structures, and these product objects have commonalities;

1.3 main functions

When users do not know the construction process and details of objects, they can directly create complex objects.

  • Users only need to specify the type and content of complex objects;
  • The builder pattern is responsible for creating complex objects in sequence (hiding the internal construction process and details)

1.4 problems solved

  • It is convenient for users to create complex objects (no need to know the implementation process)
  • Code reusability & encapsulation (encapsulating & reusing the object construction process and details)

Ex amp le: make a car & buy a car.

  1. Factory (builder mode): responsible for manufacturing cars (assembly process and details are in the factory)
  2. Car buyer (user): you just need to say the > model you need (object type and content), and then buy it directly > > to use it
    (you don't need to know how the car is assembled (wheels, doors, > engine, steering wheel, etc.)

2. Mode principle

2.1 UML class diagram & Composition

2.2 mode explanation:

  • The Director directly communicates with the Client on requirements;
  • After communication, the commander divides the customer's demand for creating products into the construction request (Builder) of each component;
  • Delegate the construction request of each component to a specific builder;
  • Each specific builder is responsible for the construction of product parts;
  • Finally, it is built into a specific Product.

3. Use the builder mode to create a shared bicycle as an example. The example code is:

Product category:

public class Bike { 

    private IFrame frame; 
    private ISeat seat; 
    private ITire tire; 
    
    public IFrame getFrame() { 
        return frame; 
    } 
    public void setFrame(IFrame frame) { 
        this.frame = frame; 
    } 
    public ISeat getSeat() { 
        return seat; 
    } 
    public void setSeat(ISeat seat) { 
        this.seat = seat; 
    } 
    public ITire getTire() { 
        return tire; 
    } 
    public void setTire(ITire tire) { 
        this.tire = tire; 
    } 
} 

Builder class:

// Abstract builder class 
public abstract class Builder { 
    abstract void buildFrame(); 
    abstract void buildSeat(); 
    abstract void buildTire(); 
    abstract Bike createBike(); 
} 

ConcreteBuilder class:

// Concrete builder class 
public class MobikeBuilder extends Builder{ 
    private Bike mBike = new Bike(); 
    @Override 
    void buildFrame() { 
        mBike.setFrame(new AlloyFrame()); 
    } 
    @Override 
    void buildSeat() { 
        mBike.setSeat(new DermisSeat()); 
    } 
    @Override 
    void buildTire() { 
        mBike.setTire(new SolidTire()); 
    } 
    @Override 
    Bike createBike() { 
        return mBike; 
    } 
} 

public class OfoBuilder extends Builder{ 
    private Bike mBike = new Bike(); 
    @Override 
    void buildFrame() { 
        mBike.setFrame(new CarbonFrame()); 
    } 
    @Override 
    void buildSeat() { 
        mBike.setSeat(new RubberSeat()); 
    } 
    @Override 
    void buildTire() { 
        mBike.setTire(new InflateTire()); 
    } 
    @Override 
    Bike createBike() { 
        return mBike; 
    } 
} 

Commander category:

public class Director { 
    private Builder mBuilder = null; 
    public Director(Builder builder) { 
        mBuilder = builder; 
    } 
    public Bike construct() { 
        mBuilder.buildFrame(); 
        mBuilder.buildSeat(); 
        mBuilder.buildTire(); 
        return mBuilder.createBike(); 
    } 
}

Client usage:

public class Click { 
    public static void main(String[] args) { 
        showBike(new OfoBuilder()); 
        showBike(new MobikeBuilder()); 
    } 
    private void showBike(Builder builder) {
        Director director = new Director(builder); 
        Bike bike = director.construct(); 
        bike.getFrame().frame(); 
        bike.getSeat().seat(); 
        bike.getTire().tire(); 
    } 
} 

The above example is the general usage of the Builder mode. The Director class plays a very important role in the Builder mode. It is used to guide the specific Builder how to build the product, control the call order, and return the complete product class to the caller. However, in some cases, it is necessary to simplify the system structure. You can combine the Director and the abstract Builder. Example code:

Abstract builder after transformation:

public abstract class NewBuilder { 
    abstract void buildFrame(); 
    abstract void buildSeat(); 
    abstract void buildTire(); 
    abstract Bike createBike(); 
    /** 
    * Merge the construct() method in the director class into the abstract builder class 
    * 
    * @return Specific product object 
    */ 
    public Bike construct() { 
        this.buildFrame(); 
        this.buildSeat(); 
        this.buildTire(); 
        return this.createBike(); 
    } 
} 

This does simplify the system structure, but it also aggravates the responsibilities of the abstract builder class, and does not conform to the principle of single responsibility. If the construct() is too complex, it is recommended to package it into the Director
In addition to the above purposes, there is another common use method, that is, when a class constructor needs to pass in many parameters, if an instance of this class is created, the code readability will be very poor, and it is easy to introduce errors. At this time, the builder mode can be used for reconstruction. The sample code before reconstruction:

// Omit getter and setter methods 
public class Computer { 
    private String cpu; 
    private String screen; 
    private String memory; 
    private String mainboard; 
    public Computer(String cpu, String screen, String memory, String mainboard) { 
        this.cpu = cpu; 
        this.screen = screen; 
        this.memory = memory; 
        this.mainboard = mainboard; 
    } 
} 
public class NewComputer { 
    private String cpu; 
    private String screen; 
    private String memory; 
    private String mainboard; 
    public NewComputer() { 
        throw new RuntimeException("can't init"); 
    } 
    private NewComputer(Builder builder) { 
        cpu = builder.cpu; 
        screen = builder.screen; 
        memory = builder.memory; 
        mainboard = builder.mainboard; 
    } 
    public static final class Builder { 
        private String cpu; 
        private String screen; 
        private String memory; 
        private String mainboard; 
        
    public Builder() {} 
    
    public Builder cpu(String val) { 
        cpu = val; 
        return this; 
    } 
    public Builder screen(String val) { 
        screen = val; 
        return this; 
    } 
    public Builder memory(String val) { 
        memory = val; 
        return this; 
    } 
    public Builder mainboard(String val) { 
        mainboard = val; 
        return this; 
    } 
    public NewComputer build() {
        return new  NewComputer(this);} 
    } 
} 

client:

public class Click { 
    public static void main(String[] args) { 
        // Non Builder mode 
        Computer computer = new Computer("cpu", "screen", "memory", "mainboard"); 
        // Builder mode 
        NewComputer newComputer = new NewComputer.Builder() 
        .cpu("cpu") 
        .screen("screen") 
        .memory("memory") 
        .mainboard("mainboard") 
        .build(); 
    } 
} 

The above example code only passes in four parameters. If the parameters are fourteen or more, the advantages of builder mode will be more obvious, the parameters will be more flexible, and the code has higher readability

4. Comparison of advantages and disadvantages

General routine: the advantage is that it is relatively simple and the development efficiency is high. The disadvantage is that if there are really many parameters, who knows what each corresponding means.

Builder mode: the advantage is that you can take the setter method name of the constructor in a way similar to annotation, so that we can clearly know what value has been set just now. It is highly readable, but the disadvantage is that it is lengthy.

advantage:

  • Using the builder mode can make the client do not have to know the details of the internal composition of the product.
  • The specific builder classes are independent of each other, which is conducive to the expansion of the system.
  • The specific builders are independent of each other, so the construction process can be gradually refined without any impact on other modules.

Disadvantages:

  • The products created by the builder model generally have more in common, and their components are similar; If there are great differences between products, it is not suitable to use the builder mode, so its scope of use is limited.
  • If the internal changes of the product are complex, it may lead to the need to define many specific builder classes to realize this change, resulting in a huge system.



Author: big front end circle
Link: https://www.jianshu.com/p/3d1c9ffb0a28
Source: Jianshu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization, and for non-commercial reprint, please indicate the source.

 

Topics: Java Design Pattern