阅读 118

设计模式系列 --建造者模式

将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示,更关注复杂对象内部类装配的顺序。

类图

image.png

  1. 指挥者:注入具体构建者,以此调用构建者的具体方法按顺序添加零件
  2. 抽象建造者:定义具体构建者需要实现的方法,以及返回产品的具体方法(抽象类)
  3. 具体建造者:实现具体产生零件的方法,为产品赋能
  4. 产品:定义要构建的产品模型,由具体的建造者填充

要点

在构造的过程中如果需要按照一定的规则来构造一个复杂的类,传统的工厂模式就不起作用了,实际上,建造者模式是在工厂方法模式中对工厂在进一步的封装,使得各个工厂的产品按照一定顺序组成一个新的产品。

要点:

  • 指挥者:规定了建造的顺序及各个部件的依赖关系
  • 抽象建造者(工厂):有实例提供的方法,规定具体建造者必须要建造的零部件

优点:

  • 继承工厂模式的解耦属性
  • 每个具体的建造者都相对独立,与其他具体的建造者无关,便于替换具体的建造者,使得具体的建造者即可获得不同的产品。
  • 可控制产品创建的具体细节
  • 指挥者类根据抽象建造者类进行编程,符合开闭。

缺点:

  • 如果产品之间差异性大无共同点,则难以抽象
  • 如果产品内部复杂,则需要定义很多建造者类

对比

  • 工厂方法生产的是产品,建造者模式是由 builder 生产零部件,再由 constructor 按条件组装成新产品。

栗子

简短而精悍的栗子

栗一

是一个手机的建造指挥的例子,定义手机必要的建造零件屏幕、电池、CPU,也就是产品的模型,在定义抽象工厂,决定各个品牌的具体工厂要干什么,之后实现具体工厂,再然后实现指挥者的建造方法,按照步骤进行构建,客户端调用各个厂商,开始建造。

class Director{
    constructor(builder){
        this.builder = builder
    }
    construct(){
        this.builder.setBattery()
        this.builder.setCpu()
        this.builder.setScreen()
        return this.builder.getProduct()
    }
} 

// 定义产品模板 
class Phone{
    constructor(){
        this.screen = '';
        this.battery =''; 
        this.cpu ='';
    }
}  

// 定义抽象建造者
class Builder{
    constructor(){
        this.phone = new Phone()
    }
    getProduct(){
        return this.phone;
    }
}

class HUAWEIBuilder extends Builder{
    constructor(){
        super()
    }
    setScreen(){
        this.phone.screen = 'huawei crreen 6.9in' 
    }
    setBattery(){
        this.phone.battery = 'huawei battery 5000mAh' 
    }
    setCpu(){
        this.phone.cpu = 'Qinlin 855' 
    }
} 

class XIAOMIBuilder extends Builder{
    constructor(){
        super()
    }
    setScreen(){
        this.phone.screen = 'xiaomi crreen 8.0in' 
    }
    setBattery(){
        this.phone.battery = 'xiaomi battery 5500mAh' 
    }
    setCpu(){
        // 各种各样的工艺 .... 
        this.phone.cpu = 'PengPai 500' 
    }
}

// 华为指挥
let huawei = new HUAWEIBuilder() 
let huaweiDirector = new Director(huawei) 
console.log(huaweiDirector.construct())

// 小米指挥
let xiaomi = new XIAOMIBuilder()
let xiaomiDirector = new Director(xiaomi)  
console.log(xiaomiDirector.construct())
复制代码

image.png

不同的调用生成不同的效果,面相接口编程。

栗二

Vue 组件的构建过程是不是和这个思想有点相似呢,可以将传入 options 比作工厂必要的数据,组件的具体格式看作产品的模板,指挥者则是 Vue 的 this.__init(option) 方法(具体工厂),执行完成后会得到一个全新的产品,那就是该组件,组件与组件之间除了参数不一样,其他骨架啥的是完全一样的,再结合建造者模式的思想想想看。

当 Vue 被 new 时开始指挥,具体建造者实际已经添加早指挥者的原型上了,只不过没有数据,注入 options 再开始建造,也不用返回产品,因为要建造的产品就是他自己

// 当 Vue 被 new 时开始指挥
function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}


// 具体建造者实际已经添加早指挥者的原型上了,也不用返回产品,因为要建造的产品就是他自己
Vue.prototype._init = function (options?: Object) {
    const vm: Component = this 
    vm._uid = uid++ 
  
    // .......
 
    /* istanbul ignore else */
    // .......
  
    // expose real self
    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')

    /* istanbul ignore if */
    // .......
  }
复制代码
文章分类
前端
文章标签