timelessover/blog

设计模式专题之工厂模式(六)

Opened this issue · 0 comments

工厂模式(Factory Pattern)

定义:创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象

简单的说

  • 将 new 操作单独封装,只对外提供相应接口
  • 遇到new 时,就要考虑是否应该使用工厂模式

工厂模式有三种形式:简单工厂模式(Simple Factory)、工厂方法模式(Factory Method)和抽象工厂模式(Abstract Factory)。在 javascript 中我们最常见的当属简单工厂模式。

作用

  • 主要用于隐藏创建实例的复杂度,只需对外提供一个接口
  • 实现构造函数和创建者的分离,满足开放封闭的原则

实例

// 定义产品
class Product {
    constructor(name) {
        this.name = name
    }
    init() {
        console.log('初始化产品')
    }
}

// 定义工厂
class Factory {
    create(name) {
        return new Product(name) // 重点!!!
    }
}
let c = new Factory()
let p = c.create('p1')
p.init()

工厂模式最直观的地方在于,创建产品对象不是通过直接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')
    }
  }
}

//调用
let superAdmin = user.getInstance('superAdmin');
let admin = user.getInstance('admin');

通过上例,我们可以看到,每次创建新的对象实例时,只需要传入相应的参数,就可以得到指定的对象实例。最直观的例子是如果不用工厂模式,那代码中是不是就会多出好多个new,这样看着也不太舒服。

其实简单工厂模式已经能满足我们前端大部分业务场景了,如果非要说其一个缺陷,那就是每次有新实例时,我们需要重写这个User大类,总归感觉和后面所述的装饰器模式有一些冲突。此时,工厂方法模式就出来了,其核心**就是独立出一个大的User类,将创建实例对象的过程用其子类来实现:

class User {
  constructor(name = '', viewPage = []) {
    this.name = name;
    this.viewPage = viewPage;
  }
}

class UserFactory extends User {
  constructor(name, viewPage) {
    super(name, viewPage)
  }
  create(role) {
    switch (role) {
      case 'superAdmin': 
        return new UserFactory( '超级管理员', ['首页', '通讯录', '发现页', '应用数据', '权限管理'] );
        break;
      case 'admin':
        return new UserFactory( '管理员', ['首页', '通讯录'] );
        break;
      default:
        throw new Error('params error');
    }
  }
}
let userFactory = new UserFactory();
let superAdmin = userFactory.create('superAdmin');
let admin = userFactory.create('admin');
let user = userFactory.create('user');

这样,虽然也得通过 new 一个实例,但至少我们可以无需修改User类里面的东西,虽说代码量上感觉和简单模式差不了多少,但**主体确实就是这样。

应用场景

jQuery选择器

$('div')new $('div') 有何区别? 为什么 $('div') 就能直接实现 new的效果,同时去除了 new $('div') 这种$('div') 去除了 new 书写繁杂的弊端,还能实现完美的链式操作代码简介,就是因为$内置的实现机制是工厂模式。其底层代码如下:

class jQuery {
    constructor(selector) {
        super(selector)
    }
    // ...
}

window.$ = function(selector) {
    return new jQuery(selector)
}

Vue 异步组件

Vue.component('async-example' , (resolve , reject) => {
    setTimeout(function() {
        resolve({
            template: `<div>I am async!</div>`
        })
    }, 1000)
})

除了上述两个常见的实例场景,还有React.createElement() 也是工厂原理。所以,当我们平时遇到要创建实例的时候,就可以想想能否用工厂模式实现了。