1 什么是建造者模式?

建造者模式(Builder)是将一个复杂对象的构建层与其表示层相互分离,同样的构建过程可采用不同的表示。

建造者模式的特点是分步构建一个复杂的对象,可以用不同组合或顺序建造出不同意义的对象,通常使用者并不需要知道建造的细节,通常使用链式调用来进行建造过程,最后调用build方法来生成最终对象。

同样作为创建型的设计模式,需要注意和工厂模式的区别,工厂虽然也是创建对象,但怎样创建无所谓,工厂模式关注的是创建的结果;而建造者模式不仅得到了结果,同时也参与了创建的具体过程,适合用来创建一个复杂的复合对象。

2 ES6中的建造者模式

下面我们来假设一个出版社的书籍后台录入系统的业务场景,书籍有四个必填信息,分别是:书名,作者,价格,分类;我们希望创建一个书籍对象返回给后端。下面我们来一步一步深入使用ES6的语法结合建造者模式创建对象。

//书籍建造者类
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
}
}
} //调用建造者类
const book = new BookBuilder()
.withName("高效能人士的七个习惯")
.withAuthor('史蒂芬·柯维')
.withPrice(51)
.withCategory('励志')
.build();

上面就通过我BookBuilder这个创建者类的写法和调用方法,但是仅仅是一个4个属性的对象,我们使用了如此多的代码去创建,这远比直接在constructor传递参数创建对象要复杂得多。这是由于在创建的过程中,我们有太多的withxxxx方法。我们其实可以自动创建这类withxxxx方法以简化代码。

//书籍建造者类
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;
}
})
} //调用建造者
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("高效能人士的七个习惯")
.withAuthor('史蒂芬·柯维')
.withPrice(51)
.withCategory('励志')
.build();

上面的BookBuilder这个类和第一个例子的效果一样,但是长度确减少不少,在有更多属性的时候,减少的代码量会更为明显。我们将所有的建造方法withxxxxconstructor调用时自动被创建,这里我们使用了一些ES6的新语法:Object.keys获取对象属性数组,...的合并对象的语法。

虽然该写法在阅读起来会比第一个方法难以理解,但是这样写法的真正作用在于,当我们需要许多的建造者类时,我们可以将上面自动创建withxxxbuild的代码提取为一个父类。在创建其他建造者类时继承该父类,这使得在创建多个建造者类时变得十分容易。

//父类
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]
}
}, {})
}
} //子类1: 书籍建造者类
class BookBuilder extends BaseBuilder {
constructor() {
super(); this.name = '';
this.author = '';
this.price = 0;
this.category = ''; super.init();
}
} //子类2: 印刷厂建造者类
class printHouseBuilder extends BaseBuilder {
constructor() {
super(); this.name = '';
this.location = '';
this.quality = ''; super.init();
}
} //调用书籍建造者类
const book = new BookBuilder()
.withName("高效能人士的七个习惯")
.withAuthor('史蒂芬·柯维')
.withPrice(51)
.withCategory('励志')
.build(); //调用印刷厂建造类
const printHouse = new printHouseBuilder()
.withName('新华印刷厂')
.withLocation('北京海淀区')
.withQuality('A')
.build();

总结

在之前提到的几种工厂模式中,他们都有一个共同特点,就是对象的创建过程不得而知,我们在调用一个函数后返回了最终的结果对象。但是在创建者模式中我们关心的是对象的创建过程,我们通常将创建复杂对象的各个类模块化,在ES6中,我们采用importexport的语法可以很灵活的引用和导出这些模块进行我们的建造模式最终生成一个结果对象。

可以看出,建造者模式的使用有且只适合创建极为复杂的对象。在前端的实际业务中,在没有这类极为复杂的对象的创建时,还是应该直接使用对象字面或工厂模式等方式创建对象。


参考内容:
[1] An Exploration of JavaScript Builders —— Ryan Oglesby
[2] 《 JavaScript设计模式 》—— 张容铭

本文转载于:猿2048从ES6重新认识JavaScript设计模式(三): 建造者模式

最新文章

  1. Linux学习心得之 LVM管理与Linux系统安装
  2. Leetcode 114, Flatten Binary Tree to Linked List
  3. 20160805_CentOS6_键盘快捷键
  4. JDK1.8 HashMap中put源码分析
  5. 简单拨号器(Android)
  6. java.lang.NoSuchMethodError: main Exception in thread "main"
  7. Android TextView文字超出一屏不能显示其它的文字 解决方案
  8. oracle 提示口令失效解决方法
  9. 小公司免费的ERP软件
  10. java虚拟机学习-JVM调优总结-垃圾回收面临的问题(8)
  11. .NET Core:使用ImageSharp跨平台处理图像
  12. 重载equals方法时要遵守的通用约定--自反性,对称性,传递性,一致性,非空性
  13. C语言 投票系统:给定候选人,从键盘输入候选人的名字,统计票数,并输出最终获胜者
  14. Java并发编程:JMM (Java内存模型) 以及与volatile关键字详解
  15. MVC DropDownList
  16. runtime实现weak属性
  17. android 系统开发板挂载U盘
  18. [LNU.Machine Learning.Question.1]梯度下降方法的一些理解
  19. 委托的begininvoke
  20. 剑指offer五十七之二叉树的下一个结点

热门文章

  1. 将ISO镜像文件制作成USB disk
  2. Hadoop3.x完全分布式搭建(详细)
  3. CSAPP-Lab01 Data Lab 记录
  4. kube-scheduler源码分析(3)-抢占调度分析
  5. 基于idea做java程序的本地k8s调试-skaffold(二)
  6. 说说如何安装 Openfire
  7. Oneops运维系统
  8. Anaconda Navigator卡logo打不开闪退问题处理方案-更换阿里云镜像源
  9. sqlmap的使用手册
  10. 09自动售货机综设实验(含按键消抖,led和状态机)