一 工厂模式和抽象工厂模式在前端开发中有那些应用?

1.1 是什么

⼯⼚模式是⽤来创建对象的⼀种最常⽤的设计模式,不暴露创建对象的具体逻辑,⽽是将逻辑封装在⼀个函数中,那么这个函数就可以被视为⼀个⼯⼚

其就像⼯⼚⼀样重复的产⽣类似的产品,⼯⼚模式只需要我们传⼊正确的参数,就能⽣产类似的产品

举个例⼦:

  • 编程中,在⼀个 A 类中通过 new 的⽅式实例化了类 B,那么 A 类和 B 类之间就存在关联(耦合)
  • 后期因为需要修改了 B 类的代码和使⽤⽅式,⽐如构造函数中传⼊参数,那么 A 类也要跟着修改,⼀个类的依赖可能影响不⼤,但若有多个类依赖了 B 类,那么这个⼯作量将会相当的⼤,容易出现修改错误,也会产⽣很多的重复代码,这⽆疑是件⾮常痛苦的事;
  • 这种情况下,就需要将创建实例的⼯作从调⽤⽅(A类)中分离,与调⽤⽅解耦,也就是使⽤⼯⼚⽅法创建实例的⼯作封装起来(减少代码重复),由⼯⼚管理对象的创建逻辑,调⽤⽅不需要知道,具体的创建过程,只管使⽤,⽽降低调⽤者因为创建逻辑导致的错误

1.2 实现

⼯⼚模式根据抽象程度的不同可以分为:

  • 简单⼯⼚模式(Simple Factory)
  • ⼯⼚⽅法模式(Factory Method)
  • 抽象⼯⼚模式(Abstract Factory)

1.2.1 简单工厂模式

简单⼯⼚模式也叫静态⼯⼚模式,⽤⼀个⼯⼚对象创建同⼀类对象类的实例

假设我们要开发⼀个公司岗位及其⼯作内容的录⼊信息,不同岗位的⼯作内容不⼀致

代码如下:

function Factory(career) {
  function User(career, work) {
    this.career = career
    this.work = work
  }
  let work
  switch (career) {
    case 'coder':
      work = ['写代码', '修Bug']
      return new User(career, work)
      break
    case 'hr':
      work = ['招聘', '员⼯信息管理']
      return new User(career, work)
      break
    case 'driver':
      work = ['开⻋']
      return new User(career, work)
      break
    case 'boss':
      work = ['喝茶', '开会', '审批⽂件']
      return new User(career, work)
      break
  }
}
let coder = new Factory('coder')
console.log(coder)
let boss = new Factory('boss')
console.log(boss)

Factory 就是⼀个简单⼯⼚。当我们调⽤⼯⼚函数时,只需要传递name、age、career就可以获取到包含⽤⼾⼯作内容的实例对象

1.2.2 工厂模式模式

⼯⼚⽅法模式跟简单⼯⼚模式差不多,但是把具体的产品放到了⼯⼚函数的 prototype 中这样⼀来,扩展产品种类就不必修改⼯⼚函数了,和⼼累就变成抽象类,也可以随时重写某种具体的产品

也就是相当于⼯⼚总部不⽣产产品了,交给下辖分⼯⼚进⾏⽣产;但是进⼊⼯⼚之前,需要有个判断来验证你要⽣产的东西是否是属于我们⼯⼚所⽣产范围,如果是,就丢给下辖⼯⼚来进⾏⽣产

如下代码:

// ⼯⼚⽅法
function Factory(career) {
  if (this instanceof Factory) {
    var a = new this[career]()
    return a
  } else {
    return new Factory(career)
  }
}
// ⼯⼚⽅法函数的原型中设置所有对象的构造函数
Factory.prototype = {
  coder: function () {
    this.careerName = '程序员'
    this.work = ['写代码', '修Bug']
  },
  hr: function () {
    this.careerName = 'HR'
    this.work = ['招聘', '员⼯信息管理']
  },
  driver: function () {
    this.careerName = '司机'
    this.work = ['开⻋']
  },
  boss: function () {
    this.careerName = '⽼板'
    this.work = ['喝茶', '开会', '审批⽂件']
  },
}
let coder = new Factory('coder')
console.log(coder)
let hr = new Factory('hr')
console.log(hr)

⼯⼚⽅法关键核⼼代码是⼯⼚⾥⾯的判断this是否属于⼯⼚,也就是做了分⽀判断,这个⼯⼚只做我能做的产品

1.2.3 抽象工厂模式

上述简单⼯⼚模式和⼯⼚⽅法模式都是直接⽣成实例,但是抽象⼯⼚模式不同,抽象⼯⼚模式并不直接⽣成实例,⽽是⽤于对产品类簇的创建

通俗点来讲就是:简单⼯⼚和⼯⼚⽅法模式的⼯作是⽣产产品,那么抽象⼯⼚模式的⼯作就是⽣产⼯⼚的

由于 JavaScript 中并没有抽象类的概念,只能模拟,可以分成四部分:

  • ⽤于创建抽象类的函数
  • 抽象类
  • 具体类
  • 实例化具体类

上⾯的例⼦中有 coder 、 hr 、 boss 、 driver 四种岗位,其中 coder 可能使⽤不同的开发语⾔进⾏开发,⽐如 JavaScript 、 Java 等等。那么这两种语⾔就是对应的类簇

let CareerAbstractFactory = function (subType, superType) {
  // 判断抽象⼯⼚中是否有该抽象类
  if (typeof CareerAbstractFactory[superType] === 'function') {
    // 缓存类
    function F() {}
    // 继承⽗类属性和⽅法
    F.prototype = new CareerAbstractFactory[superType]()
    // 将⼦类的constructor指向⽗类
    subType.constructor = subType
    // ⼦类原型继承⽗类
    subType.prototype = new F()
  } else {
    throw new Error('抽象类不存在')
  }
}

上⾯代码中 CareerAbstractFactory 就是⼀个抽象⼯⼚⽅法,该⽅法在参数中传递⼦类和⽗类,在⽅法体内部实现了⼦类对⽗类的继承

1.3 应用场景

从上⾯可看到,简单简单⼯⼚的优点就是我们只要传递正确的参数,就能获得所需的对象,⽽不需要关⼼其创建的具体细节

应⽤场景也容易识别,有构造函数的地⽅,就应该考虑简单⼯⼚,但是如果函数构建函数太多与复杂,会导致⼯⼚函数变得复杂,所以不适合复杂的情况

抽象⼯⼚模式⼀般⽤于严格要求以⾯向对象思想进⾏开发的超⼤型项⽬中,我们⼀般常规的开发的话⼀般就是简单⼯⼚和⼯⼚⽅法模式会⽤的⽐较多⼀些

综上,⼯⼚模式适⽤场景如下:

  • 如果你不想让某个⼦系统与较⼤的那个对象之间形成强耦合,⽽是想运⾏时从许多⼦系统中进⾏挑选的话,那么⼯⼚模式是⼀个理想的选择
  • 将new操作简单封装,遇到new的时候就应该考虑是否⽤⼯⼚模式;
  • 需要依赖具体环境创建不同实例,这些实例都有相同的⾏为,这时候我们可以使⽤⼯⼚模式,简化实现的过程,同时也可以减少每种对象所需的代码量,有利于消除对象间的耦合,提供更⼤的灵活性