1 what is the builder model?

When the number of constructor parameters of a class exceeds 4, and some of these parameters are optional parameters, consider using the constructor pattern.

1 what is the builder model?

Builder pattern is to separate the construction layer of a complex object from its representation layer. The same construction process can adopt different representations.

The characteristic of builder pattern is to build a complex object step by step, and can construct different objects in different combinations or orders. Usually users do not need to know the details of construction, usually use chain calls to build the process, and finally call the build method to produce the final object.

Similarly, as a creative design pattern, you should pay attention to the difference between the factory pattern and the factory pattern. Although the factory is also a creation object, it doesn't matter how to create it. The factory pattern focuses on the result of creation; The builder pattern not only gets the results, but also participates in the specific process of creation, which is suitable for creating a complex composite object.

2. Builder mode in ES6

Let's assume a business scenario of a publishing house's book background entry system. Books have four required information: book name, author, price and classification; We want to create a Book object to return to the back end. Let's go step by step and use the syntax of ES6 to create objects in combination with the builder pattern.

//Book builder class
class BookBuilder {
  constructor() {
    this.name = '';
    this.author = '';
    this.price = 0;
    this.category = '';
  }
  
  withName(name) {
    this.name = name;
    return this;
  }

  withAuthor(author) {
    this.author = author;
    return this;
  }

  withPrice(price) {
    this.price = price;
    return this;
  }

  withCategory(category) {
    this.category = category;
    return  this;
  }

  build() {
    return {
      name: this.name,
      author: this.author,
      prices: this.price,
      category: this.category
    }
  }
}

//Call builder class
const book = new BookBuilder()
  .withName("Seven habits of highly effective people")
  .withAuthor('Steven·covey ')
  .withPrice(51)
  .withCategory('Self-Improvement')
  .build();

The above is based on the writing and calling methods of the creator class BookBuilder, but it is only an object with four attributes. We use so much code to create it, which is much more complex than directly passing parameters in the constructor to create the object. This is because we have too many withxxxx methods in the creation process. We can actually automatically create such withxxxx methods to simplify the code.

//Book builder class
class BookBuilder {
  constructor() {
    this.name = '';
    this.author = '';
    this.price = 0;
    this.category = '';
  
    Object.keys(this).forEach(key => {
      const withName = `with${key.substring(0, 1).toUpperCase()}${key.substring(1)}`;
      this[withName] = value => {
        this[key] = value;
        return this;
      }
    })
  }
  
  //Call builder
  build() {
    const keysNoWithers = Object.keys(this).filter(key => typeof this[key] !== 'function');

    return keysNoWithers.reduce((returnValue, key) => {
      return {
        ...returnValue,
        [key]: this[key]
      }
    }, {})
  }
}

const book = new BookBuilder()
  .withName("Seven habits of highly effective people")
  .withAuthor('Steven·covey ')
  .withPrice(51)
  .withCategory('Self-Improvement')
  .build();

The effect of the above BookBuilder class is the same as that of the first example, but the length is reduced a lot. When there are more attributes, the reduced amount of code will be more obvious. We will automatically create all construction methods withxxxx when the constructor is called. Here we use some new syntax of ES6: object Keys get the object attribute array The syntax of the merged object.

Although this writing method is more difficult to understand than the first method, the real function of this writing method is that when we need many builder classes, we can extract the code that automatically creates withxxx and build as a parent class. Inheriting the parent class when creating other builder classes makes it easy to create multiple builder classes.

//Parent class
class BaseBuilder {
  init() {
    Object.keys(this).forEach(key => {
      const withName = `with${key.substring(0, 1).toUpperCase()}${key.substring(1)}`;
      this[withName] = value => {
        this[key] = value;
        return this;
      }
    })
  }

  build() {
    const keysNoWithers = Object.keys(this).filter(key => typeof this[key] !== 'function');

    return keysNoWithers.reduce((returnValue, key) => {
      return {
        ...returnValue,
        [key]: this[key]
      }
    }, {})
  }
}

//Subclass 1: Book builder class
class BookBuilder extends BaseBuilder {
  constructor() {
    super();

    this.name = '';
    this.author = '';
    this.price = 0;
    this.category = '';
    
    super.init();
  }
}

//Subclass 2: printer builder
class printHouseBuilder extends BaseBuilder {
  constructor() {
    super();

    this.name = '';
    this.location = '';
    this.quality = '';

    super.init();
  }
}

//Call the book builder class
const book = new BookBuilder()
  .withName("Seven habits of highly effective people")
  .withAuthor('Steven·covey ')
  .withPrice(51)
  .withCategory('Self-Improvement')
  .build();


//Call the printing factory construction class
const printHouse = new printHouseBuilder()
  .withName('Xinhua Printing Factory')
  .withLocation('Haidian District, Beijing')
  .withQuality('A')
  .build();

summary

In the several factory patterns mentioned earlier, they all have a common feature, that is, the creation process of the object is unknown. We return the final result object after calling a function. However, in the creator mode, we are concerned about the object creation process. We usually modularize various classes for creating complex objects. In ES6, we use the import and export syntax to flexibly reference and export these modules for our construction mode, and finally generate a result object.

It can be seen that the use of the builder pattern is useful and only suitable for creating extremely complex objects. In the actual business of the front end, when there is no creation of such extremely complex objects, you should directly use object literal or factory mode to create objects.

Reference:
[1]  An Exploration of JavaScript Builders - Ryan Oglesby
[2] JavaScript Design Patterns -- Zhang Rongming

Added by Stephen on Thu, 30 Dec 2021 10:48:28 +0200