JavaScript-设计模式(三) 建造者模式
建造者模式定义:将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示
解释:
看名称 我们首先想到的就是造房子,建造者模式就像施工团队,包工头和客户沟通了解客户建房需求后,在自己团队内部分发任务,将复杂的建房过程分解成若干小组,各小组分工合作最终得到需求的房子。
特点
- 创建型模式,主要用于创建对象
- 意图:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
- 适用性:当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。当构造过程必须允许被构造的对象有不同的表示时。
- 效果:可以改变一个产品的内部表示。将构造代码和表示代码分开。可以对构造过程进行更精细的控制
使用场景:
建造者模式适用于一个具有较多的零件的复杂产品的创建过程,由于需求的变化,组成这个复杂产品的各个零件经常猛烈变化,但是他们的组合方式却相对稳定。
- 相同的方法,不同的执行顺序,产生不用的结果时
- 多个部件或者零件,都可以装配到一个对象中,但是产生的结果又不相同
- 产品非常复杂,或者产品类中的调用顺序不用产生不同的作用
- 当时初始化一个对象特别复杂,参数多,而且很多参数都具有默认值时
主要角色:
- Product:产品角色
- Builder:抽象建造者
- ConcreteBuilder:具体建造者
- Director:指挥者
实现一个简单的建造者模式:
//面积
function space(m) {
//通过传入预算 决定面积
if (m < 20) {
throw Error("预算过低 ...")
}
return m / 0.8 //八千一平方
}
//厅室
function room(n) {
//根据入住人数 规划房间数量
if (n <= 0) {
throw Error("入住人数错误 ")
}
return n <= 2 ? 3 : 4;
}
//装修风格
function style(s) {
//根据客户自己喜好来决定
return s || "默认风格"
}
//建造者
/*
参数:
m 预算金额 单位,万
n 入住人数 单位,个
s 装修风格
*/
class Builder {
constructor(prop) {
console.log(prop)
//面积
this.space = space(prop.m)
//房间数量
this.room = room(prop.n)
//装饰风格
this.style = style(prop.s)
console.log(prop);
}
done() {
console.log(`建造完成,面积${this.space},房间数量${this.room},装修风格为${this.style}`);
}
}
let b1 = new Builder({ m: 100, n: 3,s:"欧美" })
let b2 = new Builder({ m: 30, n: 2 })
b1.done()
b2.done()
此时代码还是比较分散 需要我们进一步改进 便于维护,如下:
/*
参数:
m 预算金额 单位,万
n 入住人数 单位,个
s 风格
*/
//建造者
let Builder = (function () {
//面积
function space(m) {
//通过传入预算 决定面积
if (m < 20) {
throw Error("预算过低 ...")
}
return m / 0.8 //一万一平方
}
//厅室
function room(n) {
//根据入住人数 规划房间数量
if (n < 0) {
throw Error("入住人数错误 ")
}
return n <= 2 ? 3 : 4;
}
//装修风格
function style(s) {
//根据客户自己喜好来决定
return s || "默认风格"
}
//楼层
function floor(f){
//根据用户自己的喜好
return f
}
return class {
constructor(prop) {
//面积
this.space = space(prop.m)
//房间数量
this.room = room(prop.n)
//装饰风格
this.style = style(prop.s)
//楼层、
this.floor = floor(prop.f||"随机")
//console.log(prop);
//console.log(this.style);
}
done() {
console.log(`建造完成,面积${this.space},房间数量${this.room},装修风格为${this.style},楼层为${this.floor}`);
}
}
})()
let b1 = new Builder({ m: 100, n: 3, s: "欧美" })
let b2 = new Builder({ m: 40, n: 2 })
b1.done()
b2.done()
let b3 = new Builder({m:200,n:4,s:"小清新",f:4})
b3.done()
使用IIFE,将逻辑放在一起。此时,仍然没有解决对后续需求的更新,代码的可拓展性问题。
let Builder = (function (){
const BUILD = {
// 面积
space(prop){
this.space = prop.budget/0.8
},
// rooms
rooms(prop){
this.rooms = prop.number<3?3:4
},
// 风格
style(prop){
this.style = prop.style|| '默认风格'
},
// 新增楼层
floor(prop){
this.floor = prop.floor?'楼':'随机';
},
// 新增
xx(prop){
this.xx = prop.number>3?2:1
}
}
return class {
constructor(prop) {
for (const [key,value] of Object.entries(BUILD)) {
value.call(this,prop)
}
}
}
})()
let b1 = new Builder({
budget:50,
number:5,
style:"欧美"
})
let b2 = new Builder({
budget:100,
number:3,
style:"古风"
})
console.log(b1);
console.log(b2);
这个时候 后续每次新增只需要在BUILD中增加对应的属性就行了,而无需书写额外的代码,提高了代码的维护性、可拓展性~
建造者模式的优缺点
建造者模式的优点:
- 封装性好,创建和使用分离;
- 扩展性好,建造类之间独立、一定程度上解耦。
建造者模式的缺点:
- 产生多余的Builder对象;
- 产品内部发生变化,建造者都要修改,成本较大。
建造者模式和工厂模式的区别
- 建造者模式更加注重方法的调用顺序,工厂模式注重于创建对象。
- 创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的都一样。
- 关注点不一样,工厂模式只需要把对象创建出来就行了,而建造者模式不仅要创建出这个对象,还要知道这个对象由哪些部分组成。
- 建造者模式根据建造过程中的顺序不一样,最终的对象部件组成也不一样。
总结
建造者模式用来创建复杂对象,可以通过设置不同的可选参数,“定制化”地创建不同的对象。