Design pattern study notes (5) The difference between the builder pattern and other object creation methods

1. Introduction to Builder Mode

1.1 Definition of builder mode

The Builder mode refers to separating the internal appearance of a product from the product generation process, so that a construction process can generate product objects with different internal appearances. It separates object construction from representation.

That is, the process of assembling a complex object through multiple simple objects. For example, a robot requires multi-step simple assembly, and decoration requires the combination of various sub-steps:

More information acquisition: leave a message in the comment area or get it by private message, and the information is shared with everyone for free

1.2 Features of Builder Mode

  1. advantage

    • The builder mode encapsulates the details of the parts and realizes the separation of construction and representation

    • The builder can gradually refine the creation process without affecting other modules, and can easily control the risk of details

  2. Disadvantages :

    • If the product changes internally, the builder will also have to modify it as a whole, and the later maintenance costs will be relatively large

2. Builder pattern structure and implementation

2.1 The structure of the builder pattern

The builder pattern is mainly composed of four roles: Product, Abstract Builder, Concrete Builder and Director, as shown in the following class diagram:

  1. Product: A product that contains multiple components and needs to be created by a specific builder
  2. AbstractBuilder: Create an interface for a specific builder, usually including an interface that returns a product
  3. Concrete Builder: Inherit/implement the abstract builder and complete the specific creation methods of each part of the product
  4. Director: Responsible for calling the builder and the construction of internal specific product objects, which does not involve specific product information

2.2 Realization of builder mode

According to the above class diagram, we can write the following code:

//产品角色
public class Product {

    public void setPartA(){}

    public void setPartB(){}

    public void setPartC(){}

    public void show(){
        System.out.println("完成产品构建一次");
    }
}

//抽象建造者
public abstract class AbstractBuilder {

    protected Product product = new Product();

    public abstract void buildPartA();

    public abstract void buildPartB();

    public abstract void buildPartC();

    public Product getResult() {
        return product;
    }
}

//具体建造者
public class Builder1 extends AbstractBuilder{

    @Override
    public void buildPartA() {
        product.setPartA();
    }

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

    @Override
    public void buildPartC() {
        product.setPartC();
    }
}
public class Builder2 extends AbstractBuilder{

    @Override
    public void buildPartA() {
        product.setPartA();
    }

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

    @Override
    public void buildPartC() {
        product.setPartC();
    }
}

//指挥者
public class Director {

    private AbstractBuilder builder;

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

    public Product construct() {
        builder.buildPartA();
        builder.buildPartB();
        builder.buildPartC();
        return builder.getResult();
    }
}

//客户端
public class Client {
    public static void main(String[] args) {
        Builder1 builder1 = new Builder1();
        Builder2 builder2 = new Builder2();
        Director director1 = new Director(builder1);
        Director director2 = new Director(builder2);
        Product product1 = director1.construct();
        Product product2 = director2.construct();
        product1.show();
        product2.show();
    }
}

fold

3. The difference between the builder mode and other object creation methods

When it comes to application scenarios, we know that there are many ways to create objects, such as using constructors and factory patterns, so what is the difference between them? Let's talk about it separately

3.1 Constructors create objects

The constructor is the most commonly used way of creating objects. For simple objects, you only need to call the constructor to complete the object construction. But for complex objects, here's an example from the column "The Beauty of Design Patterns":

Suppose you need to define a resource pool configuration class ResourcePoolConfig. In this resource pool configuration class, there are name, maxTotal, maxIdle, minIdlefour member variables ( nameexcept , other variables are not mandatory variables), how to implement this ResourcePoolConfigclass with code:

3.1.1 Logical judgment member variables

We can if-elseuse implement the configuration of member variables:

public class ResourcePoolConfig {
  //默认配置
  private static final int DEFAULT_MAX_TOTAL = 8;
  private static final int DEFAULT_MAX_IDLE = 8;
  private static final int DEFAULT_MIN_IDLE = 0;

  private String name;
  private int maxTotal = DEFAULT_MAX_TOTAL;
  private int maxIdle = DEFAULT_MAX_IDLE;
  private int minIdle = DEFAULT_MIN_IDLE;

  public ResourcePoolConfig(String name, Integer maxTotal, Integer maxIdle, Integer minIdle) {
    if (StringUtils.isBlank(name)) {
      throw new IllegalArgumentException("name should not be empty.");
    }
    this.name = name;
	
    //分别判断每个成员变量
    if (maxTotal != null) {
      if (maxTotal <= 0) {
        throw new IllegalArgumentException("maxTotal should be positive.");
      }
      this.maxTotal = maxTotal;
    }

    if (maxIdle != null) {
      if (maxIdle < 0) {
        throw new IllegalArgumentException("maxIdle should not be negative.");
      }
      this.maxIdle = maxIdle;
    }

    if (minIdle != null) {
      if (minIdle < 0) {
        throw new IllegalArgumentException("minIdle should not be negative.");
      }
      this.minIdle = minIdle;
    }
  }
  //...省略getter方法...
}

It can be found from here that if the number of configuration items increases, the parameters in the constructor will become particularly long. It will cause a lot of burden for subsequent code management, and it is very likely to cause parameter passing errors. Is there any other way to improve it?

3.1.2 set method to set member variables

Because nameother variables are optional except for member variables, we can setset these variables through methods, allowing the caller to choose to fill in:

public class ResourcePoolConfig {
  private static final int DEFAULT_MAX_TOTAL = 8;
  private static final int DEFAULT_MAX_IDLE = 8;
  private static final int DEFAULT_MIN_IDLE = 0;

  private String name;
  private int maxTotal = DEFAULT_MAX_TOTAL;
  private int maxIdle = DEFAULT_MAX_IDLE;
  private int minIdle = DEFAULT_MIN_IDLE;
  
  public ResourcePoolConfig(String name) {
    if (StringUtils.isBlank(name)) {
      throw new IllegalArgumentException("name should not be empty.");
    }
    this.name = name;
  }

  public void setMaxTotal(int maxTotal) {
    if (maxTotal <= 0) {
      throw new IllegalArgumentException("maxTotal should be positive.");
    }
    this.maxTotal = maxTotal;
  }

  public void setMaxIdle(int maxIdle) {
    if (maxIdle < 0) {
      throw new IllegalArgumentException("maxIdle should not be negative.");
    }
    this.maxIdle = maxIdle;
  }

  public void setMinIdle(int minIdle) {
    if (minIdle < 0) {
      throw new IllegalArgumentException("minIdle should not be negative.");
    }
    this.minIdle = minIdle;
  }
  //...省略getter方法...
}

In this way, we basically solve the demand for instance objects, but if there are other requirements such as:

  • If there is a certain dependency between configuration items, the use setmethod cannot complete the dependency judgment between configuration items
  • If there nameare many required configuration items like this, there will be a problem that the list of member parameters is too long
  • If we want ResourcePoolConfigclass objects to be immutable objects, we cannot use public exposed setmethods

For the above situation, you can use the builder mode to solve:

  • Put the verification logic  Builderin the class, create it first , set the variable value Builderthrough the method , and then do centralized verification before using the method to actually create the object. Finally, the verification is successful and then the object is createdsetBuilderbuild
  • Add the construction method  privateas a private permission, so that it can only be used Builderto create ResourcePoolConfigclass objects, so no setmethods will be provided, and the created objects will be immutable objects

The specific code is as follows:

public class ResourcePoolConfig {
  private String name;
  private int maxTotal;
  private int maxIdle;
  private int minIdle;

  private ResourcePoolConfig(Builder builder) {
    this.name = builder.name;
    this.maxTotal = builder.maxTotal;
    this.maxIdle = builder.maxIdle;
    this.minIdle = builder.minIdle;
  }
  //...省略getter方法...

  //我们将Builder类设计成了ResourcePoolConfig的内部类。
  //我们也可以将Builder类设计成独立的非内部类ResourcePoolConfigBuilder。
  public static class Builder {
    private static final int DEFAULT_MAX_TOTAL = 8;
    private static final int DEFAULT_MAX_IDLE = 8;
    private static final int DEFAULT_MIN_IDLE = 0;

    private String name;
    private int maxTotal = DEFAULT_MAX_TOTAL;
    private int maxIdle = DEFAULT_MAX_IDLE;
    private int minIdle = DEFAULT_MIN_IDLE;

    public ResourcePoolConfig build() {
      // 校验逻辑放到这里来做,包括必填项校验、依赖关系校验、约束条件校验等
      if (StringUtils.isBlank(name)) {
        throw new IllegalArgumentException("...");
      }
      if (maxIdle > maxTotal) {
        throw new IllegalArgumentException("...");
      }
      if (minIdle > maxTotal || minIdle > maxIdle) {
        throw new IllegalArgumentException("...");
      }

      return new ResourcePoolConfig(this);
    }

    public Builder setName(String name) {
      if (StringUtils.isBlank(name)) {
        throw new IllegalArgumentException("...");
      }
      this.name = name;
      return this;
    }

    public Builder setMaxTotal(int maxTotal) {
      if (maxTotal <= 0) {
        throw new IllegalArgumentException("...");
      }
      this.maxTotal = maxTotal;
      return this;
    }

    public Builder setMaxIdle(int maxIdle) {
      if (maxIdle < 0) {
        throw new IllegalArgumentException("...");
      }
      this.maxIdle = maxIdle;
      return this;
    }

    public Builder setMinIdle(int minIdle) {
      if (minIdle < 0) {
        throw new IllegalArgumentException("...");
      }
      this.minIdle = minIdle;
      return this;
    }
  }
}

// 这段代码会抛出IllegalArgumentException,因为minIdle>maxIdle
ResourcePoolConfig config = new ResourcePoolConfig.Builder()
        .setName("dbconnectionpool")
        .setMaxTotal(16)
        .setMaxIdle(10)
        .setMinIdle(12)
        .build();

fold

In addition, the creation of objects in the builder mode can also prevent objects from having an invalid state. For example, a rectangle class must have two attributes of length and width to be a valid rectangle, and if there is only one attribute, the object has no meaning. Therefore, in the builder mode, first set the builder's variables and then create the object at one time, which can ensure that the object is always in a valid state.

3.2 Differences from factory mode

  1. Factory pattern : used to create different but related types of objects (a group of subclasses that inherit the same parent class or interface), and the given parameters determine which type of object to create.
  2. Creator mode : used to create a type of complex object, and create different objects "customized" by setting different optional parameters

4. Application Scenarios of Builder Mode

4.1 JDK source code

4.1.1  java.lang.StringBuilderand  java.lang.StringBuffer#

The operations on strings in these two classes use the builder pattern, such as the method java.lang.StringBuilderin append:

@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}
//通过调用AbstractStringBuilder中的append 方法来构造对象
public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}

JAVA copy full screen

In addition, the application of the builder mode should be used according to different businesses. For example, if there is only one type of product created, then only one specific builder is needed. At this time, the abstract builder can also be omitted, and even the commander can be omitted. character of.


More information acquisition: leave a message in the comment area or get it by private message, and the information is shared with everyone for free

Guess you like

Origin blog.csdn.net/weixin_45536242/article/details/125764794