强烈推荐大神写的文章:从ES6重新认识设计模式,从ES5、ES6的角度剖析了设计模式,还给出了应用实例。此篇文章就不介绍ES6的写法了,重点在于设计模式的思想。
简单工厂
简单工厂模式,又称为静态工厂方法,可以这样子来理解,工厂就是售货员,我们要买什么东西只需要告诉售货员,让他来拿给你,我们不需要去关注这个东西摆放在哪里,或者价格如何。
具体的实现分为两种:
- 通过类实例化对象创建
//简单工厂
let UserFactory = function (role) {
function SuperAdmin() {
this.name = "超级管理员",
this.viewPage = ['首页', '通讯录', '发现页', '应用数据', '权限管理']
}
function Admin() {
this.name = "管理员",
this.viewPage = ['首页', '通讯录', '发现页', '应用数据']
}
function NormalUser() {
this.name = '普通用户',
this.viewPage = ['首页', '通讯录', '发现页']
}
switch (role) {
case 'superAdmin':
return new SuperAdmin();
break;
case 'admin':
return new Admin();
break;
case 'user':
return new NormalUser();
break;
default:
throw new Error('参数错误, 可选参数:superAdmin、admin、user');
}
}
//调用
let superAdmin = UserFactory('superAdmin');
let admin = UserFactory('admin')
let normalUser = UserFactory('user')
UserFactory就是一个简单工厂,在该函数中有3个构造函数分别对应不同的权限的用户。当我们调用工厂函数时,只需要传递superAdmin, admin, user这三个可选参数中的一个获取对应的实例对象
- 通过一个新对象然后包装增强其属性和功能
function createBook(name,time,type){
var o = new Object();
o.name = name;
o.time = time;
o.type = type;
o.getName = function(){
console.log(this.name);
};
return o;
}
var book = createBook("js book",2014,"js");
book.getName();
有点像是寄生式继承
工厂方法模式
由于每次有新的需求增加,都需要去修改工厂和添加相关的类,难以维护,简单工厂只能作用于创建的对象数量较少,对象的创建逻辑不复杂时使用。
我们来看看安全工厂方法
//安全模式创建的工厂方法函数
let UserFactory = function(role) {
if(this instanceof UserFactory) {
var s = new this[role]();
return s;
} else {
return new UserFactory(role);
}
}
//工厂方法函数的原型中设置所有对象的构造函数
UserFactory.prototype = {
SuperAdmin: function() {
this.name = "超级管理员",
this.viewPage = ['首页', '通讯录', '发现页', '应用数据', '权限管理']
},
Admin: function() {
this.name = "管理员",
this.viewPage = ['首页', '通讯录', '发现页', '应用数据']
},
NormalUser: function() {
this.name = '普通用户',
this.viewPage = ['首页', '通讯录', '发现页']
}
}
//调用
let superAdmin = UserFactory('SuperAdmin');
let admin = UserFactory('Admin')
let normalUser = UserFactory('NormalUser')
所谓的安全模式类,就是说如果有人不知道这个对象是一个类,那么在使用的时候可能会忽略new关键字而直接去执行这个类。为了避免这种情况,我们在构造函数开始时先判断this指代是不是类,如果不是的话,当然是指向window,就要帮他创建对象并返回。
工厂方法模式:将实际创建对象工作推迟到子类当中。这样有新需求的时候,只需要在工厂类的原型里面做修改就可以了。
抽象工厂
JavaScript的abstract是一个保留字,并没有提供抽象类,抽象类是一种声明但不能使用的类,当你使用的时候就会报错。ES6也没有实现abstract,但是我们可以使用new.target来模拟出抽象类,而在ES5中,我们可以在类的方法中手动地抛出错误来模拟抽象类。
let WechatUser = function() {}
WechatUser.prototype = {
getName: function() {
return new Error('抽象方法不能调用');
}
}
在抽象工厂中,类簇一般用父类定义,并在父类中定义一些抽象方法,再通过抽象工厂让子类继承父类。所以,抽象工厂是用来创建子类的,是实现子类继承父类的方法。下面是ES5的写法。
let AccountAbstractFactory = function(subType, superType) {
//判断抽象工厂中是否有该抽象类
if(typeof AccountAbstractFactory[superType] === 'function') {
//缓存类
function F() {};
//继承父类属性和方法
F.prototype = new AccountAbstractFactory[superType] ();
//将子类的constructor指向子类
subType.constructor = subType;
//子类原型继承父类
subType.prototype = new F();
} else {
throw new Error('抽象类不存在!')
}
}
//微信用户抽象类
AccountAbstractFactory.WechatUser = function() {
this.type = 'wechat';
}
AccountAbstractFactory.WechatUser.prototype = {
getName: function() {
return new Error('抽象方法不能调用');
}
}
//qq用户抽象类
AccountAbstractFactory.QqUser = function() {
this.type = 'qq';
}
AccountAbstractFactory.QqUser.prototype = {
getName: function() {
return new Error('抽象方法不能调用');
}
}
//新浪微博用户抽象类
AccountAbstractFactory.WeiboUser = function() {
this.type = 'weibo';
}
AccountAbstractFactory.WeiboUser.prototype = {
getName: function() {
return new Error('抽象方法不能调用');
}
}
实例化的做法是:定义子类,然后让子类继承相应的产品簇抽象类。这里就不贴代码了。
抽象工厂是唯一一种抽象化创建模式,区别于简单工厂创建单一对象,工厂方法创建多类对象。
目前ES6的与语法糖class和extend已经带给我们很多方便,但由于JavaScript中不支持抽象化创建和虚拟方法,抽象工厂模式也就难以广泛应用,而且工厂模式本身是用于大型工程项目的。