The code has been written for several years, and the design pattern is in a state of forgetting and forgetting. Recently, I have some feelings about the design pattern, so I will learn and summarize it again.

Most speak design pattern articles are using Java, c + + such based on the class the static type of language, as a front-end developer, js this prototype based dynamic language, function has become a first class citizen, slightly different on some design patterns, even simple to don’t like to use the design patterns, sometimes also can produce some confusion.

The following are summarized in the order of “scenario” – “Design Pattern definition” – “code implementation” – “more scenarios” – “total”. If there are any improper points, welcome to discuss.

scenario

If we define a function:

function getPhone(size, type, screen, price=100) {... }Copy the code

That’s fine if the function is stable, but if it’s constantly changing, like adding new parameters.

function getPhone(size, type, screen, price=100, discount) {... }Copy the code

GetPhone (4.3, ‘iOS’, ‘OLED’, undefined, 0.8) must be explicitly called if we want to use the default value of price.

If you add another parameter with a default value, it will look more and more strange.

function getPhone(size, type, screen, price=100, discount, mode='test') {... }Copy the code

If the function has been called in many places, you need to make sure that the rest of the parameters are properly passed.

At this point, the idea of the builder model can be used to transform it.

Builder model

Take a look at wikipedia’s definition:

The builder pattern is a design pattern designed to provide a flexible solution to various object creation problems in object-oriented programming. The intent of the Builder design pattern is to separate the construction of a complex object from its representation. It is one of the Gang of Four design patterns.

The Builder pattern is a creative design pattern, that is, to generate objects. It takes the complex creation process out of the constructors, and you can then create a variety of objects without changing the original constructors.

The GoF book provides a way to create a new Builder class that delegates object creation to the Builder class. The original class does nothing but calls.

The Director class holds an instance of Builder in the constructor, and then calls buildPart and getResult of the Builder class to create the object. If new objects need to be created in the future, you just need to implement the new Builder class without modifying the Director instance.

The original Builder mode took object creation completely out of the Builder class, which would have made the original class useless. Maybe we could not pull it all out, and the Builder class would just take the parameters.

The following example is from Geek TimeThe beauty of design patterns

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; }}/ /... Omit getter methods...
}
Copy the code

The ResourcePoolConfig class constructor above takes four parameters, and if it changes frequently, it may have more in the future, making the code less readable and easier to use. So you can use the builder mode here, but the builder mode is only used to pass parameters, and the rest of the logic remains in the ResourcePoolConfig class.

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;
  }
  / /... Omit getter methods...

  // We designed the Builder class to be an inner class of ResourcePoolConfig.
  / / we can also be ResourcePoolConfigBuilder Builder class designed to separate the inner class.
  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(a) {
      // Check logic here, including mandatory check, dependency check, constraint check, etc
      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; }}}// This code throws IllegalArgumentException because minIdle>maxIdle
ResourcePoolConfig config = new ResourcePoolConfig.Builder()
        .setName("dbconnectionpool")
        .setMaxTotal(16)
        .setMaxIdle(10)
        .setMinIdle(12)
        .build();
Copy the code

So we can use ResourcePoolConfig. Builder () to set the parameters, the generated parameter object is passed to the ResourcePoolConfig constructor of a class.

Instead of creating different Builder classes to create objects, we pass different parameters to the Builder class to create different objects.

Code implementation

Only the builder mode of variants is discussed here.

In JS, we could also do the same by introducing a Builer class that accepts parameters and then passing the created parameter object to the original class.

But the reason why we introduced the new Builder class in Java is that Java can only create objects through classes, but in JS we can create objects through literals, and ES6 also provides object deconstruction syntax, which makes it more concise to use.

We simply aggregate the argument list into an object and deconstruct the arguments.

function getPhone(size, type, screen, price=100, discount) {
  console.log("size", size);
  console.log("type", type);
  console.log("screen", screen);
  console.log("price", price);
  console.log("discount", discount);
}
Copy the code

We just need to change it to:

function getPhone({ size, type='iOS', screen='OLED', price = 100, discount } = {}) {
    console.log("size", size);
    console.log("type", type);
    console.log("screen", screen);
    console.log("price", price);
    console.log("discount", discount);
}

getPhone({ size: 4.discount: 0.1.type: 'android' }); // Just pass the required parameters
Copy the code

It is easy to set the default values, and the order of arguments is not important, and you do not need to worry too much about whether passing arguments will cause problems when calling other places in the future.

Notice the {… } = {} better write the curly braces, otherwise if the user calls the function without passing anything, the deconstruction will fail directly.

function getPhone({ size, type='iOS', screen='OLED', price = 100, discount }) {
    console.log("size", size);
    console.log("type", type);
    console.log("screen", screen);
    console.log("price", price);
    console.log("discount", discount);
}

getPhone()
Copy the code

More scenes

Passing parameters through objects In addition to functions, when designing a component, if the parameters of the component are constantly changing and increasing, we might as well introduce an Object parameter and then pass the related parameters into Object.

The total

– it’s not clear if the original builder mode has any practical application, it hasn’t been encountered yet, I’ll add it in the future.

The builder mode of the variant (passing parameters only) is also simple in JS, passing parameters directly through objects.

Recommended reading for more design patterns: Pattern.windliang. wang/