工厂模式

155 阅读4分钟

什么是工厂模式

提供一系列相关或相互依赖的对象接口,而不需指定他们具体的类 包含三种模式,分别为简单工厂模式,工厂方法模式,抽象工厂模式。在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)
}

后续

上面说到的三种工厂模式和上文的单例模式一样,都是属于创建型的设计模式。简单工厂模式又叫静态工厂方法,用来创建某一种产品对象的实例,用来创建单一对象;工厂方法模式是将创建实例推迟到子类中进行;抽象工厂模式是对类的工厂抽象用来创建产品类簇,不负责创建某一类产品的实例。

在实际的业务中,需要根据实际的业务复杂度来选择合适的模式。对于非大型的前端应用来说,灵活使用简单工厂其实就能解决大部分问题。