概念
将一个复杂对象的构建层与其表示层分离,同样的构建过程可以采用不同的并表示
工厂模式主要是为了创建对象实例或者类簇(抽象工厂),关心的是最终产出(创建)的是什么,而不关心创建的过程。而建造者模式关心的是创建这个对象的整个过程,甚至于创建对象的每一个细节。 以下以创建应聘者为例:应聘者有兴趣爱好,姓名和期望的职位等等
实现
// 创建一个人
function PersonBuilder(params) {
this.skill = (params && params.skill) || "保密";
this.hbody = (params && params.hbody) || "保密";
}
// 人的原型方法
PersonBuilder.prototype = {
getSkill: function () {
console.log(this.skill);
},
getHbody: function () {
console.log(this.hbody);
},
};
应聘者有姓名和工作,先实例化其姓名类和工作类:
//实例化姓名类
function Named(name) {
var that = this;
(function (name, that) {
that.wholeName = name;
if (name.indexOf(" ") > -1) {
that.firstName = name.slice(0, name.indexOf(" "));
that.secondName = name.slice(name.indexOf(" "));
}
})(name, that);
}
//实例化职位类
var Work = function (work) {
var that = this;
//构造器
//构造函数中通过传入的职位特征来设置相应职位以及描述
(function (work, that) {
switch (work) {
case "code":
that.work = "工程师";
that.workDescript = "每天沉醉于编程";
break;
case "UI":
case "UE":
that.work = "设计师";
that.workDescript = "设计更似一种艺术";
break;
case "teach":
that.work = "教师";
that.workDescript = "分享也是一种快乐";
break;
default:
that.work = work;
that.workDescript = "对不起,我们还不清楚您所选择职位的相关描述";
}
})(work, that);
};
//更换期望的职位
Work.prototype.changeWork = function (work) {
this.work = work;
};
//添加对职位的描述
Work.prototype.changeDescript = function (sentence) {
this.workDescript = sentence;
};
这样就创建了抽象出来的3个类:应聘者类,姓名解析类和期望职位类。可以通过对这三个类的组合调用,写一个建造者类来创建出一个完整的应聘对象。
function Person(name, work) {
var _person = new PersonBuilder();
_person.name = new Named(name);
_person.work = new Work(work);
return _person;
}
var person = new Person("xiao ming", "code");
console.log(person);
person.work.changeDescript("更改描述!");
console.log(person.work.workDescript); //更改描述!
通过观察可以发现,建造者模式和工厂模式是有所不同的,建造者模式不仅可以得到创建的结果,而且参与了创建的具体过程,也干涉了创建的具体实现的细节。
ES6 实现
利用 class 语法实现 PersonBuilder,Named,Work 3 个类。
class PersonBuilder {
constructor(params) {
this.skill = (params && params.skill) || "保密";
this.hbody = (params && params.hbody) || "保密";
}
getSkill() {
console.log(this.skill);
}
getHbody() {
console.log(this.hbody);
}
}
class Named {
constructor(params) {
const that = this;
(function (name, that) {
that.wholeName = name;
if (name.indexOf(" ") > -1) {
that.firstName = name.slice(0, name.indexOf(" "));
that.secondName = name.slice(name.indexOf(" "));
}
})(params.name, that);
}
}
class Work {
constructor(params) {
const that = this;
//构造器
//构造函数中通过传入的职位特征来设置相应职位以及描述
(function (work, that) {
switch (work) {
case "code":
that.work = "工程师";
that.workDescript = "每天沉醉于编程";
break;
case "UI":
case "UE":
that.work = "设计师";
that.workDescript = "设计更似一种艺术";
break;
case "teach":
that.work = "教师";
that.workDescript = "分享也是一种快乐";
break;
default:
that.work = work;
that.workDescript = "对不起,我们还不清楚您所选择职位的相关描述";
}
})(params.work, that);
}
changeWork(work) {
this.work = work;
}
changeDescript(sentence) {
this.workDescript = sentence;
}
}
去创造一个人的时候,需要同时继承上面的三个类,但是 JavaScript 语法是不允许同时继承多个类,例如:
class Person extends PersonBuilder,Named,Work {
super( name,work )
}
这样的写法会抛出异常,所以我们需要来封装一个函数 mix,来实现多继承。
Mixins多继承
Mixin 指的是多个对象合成一个新的对象,新对象具有各个组成成员的接口。
function mix(...mixins) {
class Mix {
// 如果不需要拷贝实例属性下面这段代码可以去掉;
constructor(params) {
for (let mixin of mixins) {
copyProperties(this, new mixin(params)); // 拷贝实例属性
}
}
}
for (let mixin of mixins) {
copyProperties(Mix, mixin); // 拷贝静态属性
copyProperties(Mix.prototype, mixin.prototype); // 拷贝原型属性
}
return Mix;
}
// 深拷贝
function copyProperties(target, source) {
for (let key of Reflect.ownKeys(source)) {
if (key !== "constructor" && key !== "prototype" && key !== "name") {
let desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
}
使用方法:
class Person extends mix(PersonBuilder, Named, Work) {
constructor(name, work) {
super({ name, work });
}
}