建造者模式

729 阅读4分钟

基本概念

建造者模式(Builder Pattern)又称生成器模式,分步构建一个复杂对象,并允许按步骤构造。同样的构建过程可以采用不同的表示,将一个复杂对象的构建层与其表示层分离

在工厂模式中,我们并不关心创建过程,只关心创建的结果,其创建的结果也都是一个整体。而建造者模式则关心的是对象创建的过程,因此我们通常将创建的复杂对象的模块化,使得被创建的对象的每一个子模块都可以得到高质量的复用。

现实生活中的例子

造车:

我们要早一台车,而车的零部件是非常多的,比如:发动机、轮子、车身、内饰等等。一台车是由这些零件分别生产好,再一件一件拼装起来的,不可能一气呵成整体去生产一台车的所有。造车的过程就非常符合建造者模式的概念。

造手机:

生产一部手机也需要很多零件,主板、显示屏、外壳、芯片等等,每一个零件都装配好久制造出了一部手机。

类似的例子还有很多,就不一一列举了。

应用场景

  1. 相同的方法,不同的执行顺序,产生不一样的产品时,可以采用建造者模式;
  2. 产品的组成部件类似,通过组装不同的组件获得不同产品时,可以采用建造者模式;

优缺点

优点:

  1. 使用建造者模式可以使产品的构建流程和产品的表现分离,也就是将产品的创建算法和产品组成的实现隔离,访问者不必知道产品部件实现的细节;
  2. 扩展方便,如果希望生产一个装配顺序或方式不同的新产品,那么直接新建一个指挥者即可,不用修改既有代码,符合开闭原则;
  3. 更好的复用性,建造者模式将产品的创建算法和产品组成的实现分离,所以产品创建的算法可以复用,产品部件的实现也可以复用,带来很大的灵活性;

缺点:

  1. 建造者模式一般适用于产品之间组成部件类似的情况,如果产品之间差异性很大、复用性不高,那么不要使用建造者模式;
  2. 实例的创建增加了许多额外的结构,无疑增加了许多复杂度,如果对象粒度不大,那么我们最好直接创建对象;

实现

实现建造者模式通常需要知道几个概念:

  1. Director,指挥者,调用各种零部件,实现装配工作,最后返回成品。

  2. Builder,建造者,含有不同部件的生成方式,供指挥者调用,但不带任何装配流程。

  3. Product,产品,要返回给访问者的复杂对象。

以造车为例:汽车厂家就是指挥者(Director),负责将不同的部件组装成最后的产品(Product),而部件的供应商,比如生成车轮的厂家就相当于建造者(Builder)。

结合链式模式,代码如下:

class CarBuilder {
    constructor(weight, height, color, name, type) {
        this.weight = weight
        this.height = height
        this.color = color
        this.name = name
        this.type = type
    }

    buildTyre(type) {
        const tyre = {}
        switch (type) {
            case 'small':
                tyre.tyreType = '小号轮胎'
                tyre.tyreIntro = '正在使用小号轮胎'
                break
            case 'normal':
                tyre.tyreType = '中号轮胎'
                tyre.tyreIntro = '正在使用中号轮胎'
                break
            case 'big':
                tyre.tyreType = '大号轮胎'
                tyre.tyreIntro = '正在使用大号轮胎'
                break
            default:
                break
        }
        this.tyre = tyre
        return this
    }

    buildEngine(type) {
        const engine = {}
        switch (type) {
            case 'small':
                engine.engineType = '小马力发动机'
                engine.engineIntro = '正在使用小马力发动机'
                break
            case 'normal':
                engine.engineType = '中马力发动机'
                engine.engineIntro = '正在使用中马力发动机'
                break
            case 'big':
                engine.engineType = '大马力发动机'
                engine.engineIntro = '正在使用大马力发动机'
                break
            default:
                break
        }
        this.engine = engine
        return this
    }

    setPropertyFuncChain() {
        Object.getOwnPropertyNames(this)
            .forEach(key => {
                const funcName = 'set' + key.replace(/^\w/g, str => str.toUpperCase())
                this[funcName] = value => {
                    this[key] = value
                    return this
                }
            })
        return this
    }
}

class CarFactory1 {
    constructor() {
        return new CarBuilder().setPropertyFuncChain()
            .setWeight('2ton')
            .setHeight('2000mm')
            .setColor('white')
            .setName('car1')
            .setType('AMG')
            .buildTyre('small')
            .buildEngine('big')
    }
}

class CarFactory2 {
    constructor() {
        return new CarBuilder().setPropertyFuncChain()
            .setWeight('2.1ton')
            .setHeight('2010mm')
            .setColor('white')
            .setName('car2')
            .setType('AMG')
            .buildTyre('normal')
            .buildEngine('normal')
    }
}

const car1 = new CarFactory1()
const car2 = new CarFactory2()
console.log(car1)
console.log(car2)