将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示,更关注复杂对象内部类装配的顺序。
类图
- 指挥者:注入具体构建者,以此调用构建者的具体方法按顺序添加零件
- 抽象建造者:定义具体构建者需要实现的方法,以及返回产品的具体方法(抽象类)
- 具体建造者:实现具体产生零件的方法,为产品赋能
- 产品:定义要构建的产品模型,由具体的建造者填充
要点
在构造的过程中如果需要按照一定的规则来构造一个复杂的类,传统的工厂模式就不起作用了,实际上,建造者模式是在工厂方法模式中对工厂在进一步的封装,使得各个工厂的产品按照一定顺序组成一个新的产品。
要点:
- 指挥者:规定了建造的顺序及各个部件的依赖关系
- 抽象建造者(工厂):有实例提供的方法,规定具体建造者必须要建造的零部件
优点:
- 继承工厂模式的解耦属性
- 每个具体的建造者都相对独立,与其他具体的建造者无关,便于替换具体的建造者,使得具体的建造者即可获得不同的产品。
- 可控制产品创建的具体细节
- 指挥者类根据抽象建造者类进行编程,符合开闭。
缺点:
- 如果产品之间差异性大无共同点,则难以抽象
- 如果产品内部复杂,则需要定义很多建造者类
对比
- 工厂方法生产的是产品,建造者模式是由 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())
不同的调用生成不同的效果,面相接口编程。
栗二
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 */
// .......
}