什么是工厂模式
提供一系列相关或相互依赖的对象接口,而不需指定他们具体的类 包含三种模式,分别为简单工厂模式,工厂方法模式,抽象工厂模式。在js中我们主要用到前两种。
工厂模式的设计思想
- 将new操作单独封装,只对外提供调用接口
- 遇到new的时候应该考虑使用工厂模式
作用
- 隐藏构建对象的复杂度,对外只提供相应的接口
- 实现构造器和创造器的分离,符合开放封闭原则
工厂模式实现
1、简单工厂模式(Simple Factory)
简单工厂模式,有叫静态工厂方法,有一个工厂对象决定创建某一种产品类的实例,即用来创建同一类对象
工厂方式最直观的体现是不通过new的方式调用,而是通过一个工厂方法
下面看个简单公司的例子
// User 用户类
class User {
constructor(opt) {
this.name = opt.name;
this.viewPage = opt.viewPage;
}
static getInstance(role) {
switch (role) {
case 'superAdmin':
return new User({ name: '超级管理员', viewPage: ['首页', '通讯录', '发现页', '应用数据', '权限管理'] });
break;
case 'admin':
return new User({ name: '管理员', viewPage: ['首页', '通讯录'] });
break;
default:
throw new Error('params error')
}
}
}
//调用
const superAdmin = User.getInstance('superAdmin');
const admin = User.getInstance('admin');
通过上例,我们看到每次创建新实例的时候,只需要提供相应的参数,就可以得到对应的实例。最直观的体验是用了工厂方法后,少些了好多new关键字。
其实简单工厂模式已经可以满足前端大多数场景,有一个缺陷就是,每次新增角色的时候,需要修改User大类,不友好。有点违反开放封闭原则,那么就需要我们的工厂方法模式了。
2、工厂方法模式(Factory Method)
工厂方法模式,是建立抽象核心类,将创建实例的实际重心放在核心抽象大类的子类中;
class User {
constructor({name, viewPage}) {
if(new.target === User) {
throw new Error('抽象类不能实例化!');
}
this.name = name;
this.viewPage = viewPage;
}
}
class UserFactory extends User {
constructor ({name = '', viewPage = []} = {}) {
// 安全模式
if(new.target === UserFactory) {
super({name, viewPage})
} else {
return new UserFactory({name, viewPage});
}
}
create(role) {
switch (role) {
case 'superAdmin':
return new UserFactory({ name: '超级管理员', viewPage: ['首页', '通讯录', '发现页', '应用数据', '权限管理'] });
break;
case 'admin':
return new UserFactory({ name: '管理员', viewPage: ['首页', '通讯录'] });
break;
default:
throw new Error('params error')
}
}
}
const userFactory = new UserFactory()
const superAdmin = userFactory.create('superAdmin');
const admin = userFactory.create('admin');
3、抽象工厂模式(Abstract Factory)
上面介绍了简单工厂模式和工厂方法模式,但是抽象工厂模式不会直接生成实例,而是用于创建产品类簇。
上面例子中,分别有超管,管理员,用户3中角色,但是这些角色也可能来自不同的社交账户注册,wechat,qq,weibo。在抽象工厂中,类簇用父类定义,在抽象工厂中抽象出公共方法,在让子类继承父类方法。
class User {
constructor(type) {
if (new.target === User) {
throw new Error('抽象类不能实例化!')
}
this.type = type;
}
}
class UserOfWechat extends User {
constructor(name) {
super('wechat');
this.name = name;
this.viewPage = ['首页', '通讯录', '发现页']
}
}
class UserOfQq extends User {
constructor(name) {
super('qq');
this.name = name;
this.viewPage = ['首页', '通讯录', '发现页']
}
}
class UserOfWeibo extends User {
constructor(name) {
super('weibo');
this.name = name;
this.viewPage = ['首页', '通讯录', '发现页']
}
}
function getAbstractUserFactory(type) {
switch (type) {
case 'wechat':
return UserOfWechat;
break;
case 'qq':
return UserOfQq;
break;
case 'weibo':
return UserOfWeibo;
break;
default:
throw new Error('参数错误, 可选参数:superAdmin、admin、user')
}
}
let WechatUserClass = getAbstractUserFactory('wechat');
let QqUserClass = getAbstractUserFactory('qq');
let WeiboUserClass = getAbstractUserFactory('weibo');
let wechatUser = new WechatUserClass('微信小李');
let qqUser = new QqUserClass('QQ小李');
let weiboUser = new WeiboUserClass('微博小李');
应用场景
jQuery 中的$('div')。试想下,如果不是使用 $('div') 而是使用 new $('div') ,就会很烦人。因此jQuery也是利用了工厂方法
class jQuery {
constructor(selector) {
super(selector)
return this
}
...
}
windwo.$ = function (selector) {
return new jQuery(selector)
}
React.createElement()方法就是一个工厂方法,Vue类似
class vNode {
constructor(selector) {
super(selector)
}
...
}
React.createElement = function (tag, attrs, childern) {
return new vNode(tag, attrs, childern)
}
后续
上面说到的三种工厂模式和上文的单例模式一样,都是属于创建型的设计模式。简单工厂模式又叫静态工厂方法,用来创建某一种产品对象的实例,用来创建单一对象;工厂方法模式是将创建实例推迟到子类中进行;抽象工厂模式是对类的工厂抽象用来创建产品类簇,不负责创建某一类产品的实例。
在实际的业务中,需要根据实际的业务复杂度来选择合适的模式。对于非大型的前端应用来说,灵活使用简单工厂其实就能解决大部分问题。