【设计模式系列】二、工厂模式

在上一篇 什么是设计模式? 中,我们介绍了什么是设计模式,设计模式的基本原则与常见分类。

我们接下来陆续介绍具体的设计模式和使用方法,今天给大家带来的是“工厂模式”的介绍。

什么是工厂模式?

工厂模式是一种非常常用的创建型设计模式,其提供了创建对象的最佳方式。在创建对象时,不会对客户端暴露对象的创建逻辑,而是通过使用共同的接口来创建对象。


工厂模式就是隐藏了创建一个实例的复杂度,只需要提供一个简单的接口调用,直接完成创建实例的目的。


工厂模式一般分为三类:

  • 简单工厂模式(Simple Factory)

简单工厂模式(Simple Factory Pattern):专门定义一个类(工厂类)来负责创建其他类的实例。可以根据创建方法的参数来返回不同类的实例,被创建的实例通常都具有共同的父类。创建型的工厂模式一共分为三种:

  • 工厂方法模式(Factory Method)

工厂方法模式(Factory Method Pattern)又称为工厂模式,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,即通过不同的工厂子类来创建不同的产品对象。

  • 抽象工厂模式(Abstract Factory)

抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。

其实这三种工厂模式的区别,我认为没有太大必要去细分,重点还是在于应用。


实际需求

假如有这样一个需求:

我们想在网页面里插入一些元素,而这些元素类型不固定,可能是图片,也有可能是连接,甚至可能是文本,根据工厂模式的定义,我们需要定义工厂类和相应的子类,我们先来定义子类的具体实现(也就是子函数):

let page = page || {};
page.dom = page.dom || {};
//子函数1:处理文本
page.dom.Text = function ({
    this.insert = function (where{
        var txt = document.createTextNode(this.url);
        where.appendChild(txt);
    };
};

//子函数2:处理链接
page.dom.Link = function ({
    this.insert = function (where{
        var link = document.createElement('a');
        link.href = this.url;
        link.appendChild(document.createTextNode(this.url));
        where.appendChild(link);
    };
};

//子函数3:处理图片
page.dom.Image = function ({
    this.insert = function (where{
        var im = document.createElement('img');
        im.src = this.url;
        where.appendChild(im);
    };
};

那么我们如何定义工厂处理函数呢?其实很简单:

page.dom.factory = function (type{
    return new page.dom[type];
}

使用方式如下:

let o = page.dom.factory('Link');
o.url = 'http://www.cnblogs.com';
o.insert(document.body);

应用

比如一个非常常见的场景,就是 jQuery 的选择器:

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

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

这样的代码非常明显,我们需要构建一个 jQuery 实例时,只需要:

$('selector')

因为  作为一个函数,它直接返回了 new jQuery(selector),开发者不需要再麻烦地使用 new $('selector') 方式。

除了 jQuery 以外,React 开发者常用的 React.createElement 也是工厂模式的体现:

React.createElement('span'null'Factory Pattern!')

只要具有组件化思想的类库或者框架,无一例外都会使用工厂模式去创建组件实例,除了提到的 React、jQuery(jQuery UI) 以外,Vue,甚至是更古老的 ExtJS 都不例外。

例如:

class Car {
  constructor(options) {
    const {doors = 4, state = 'new', color = 'black'} = options
    this.doors = doors
    this.state = state
    this.color = color
  }
 }
 
 class Truck {
  constructor(options) {
    const {wheelSize = 'medium', state = 'used', color = 'silver'} = options
    this.wheelSize = wheelSize
    this.state = state
    this.color = color
  }
 }
 
 class VehicleFactory {
  createVehicle(options) {
    switch (options.type) {
      case 'car':
        this.vehicleTarget = Car
        break;
 
      case 'truck':
        this.vehicleTarget = Truck
        break;
 
      default:
        this.vehicleTarget = Car
        break;
    }
    return new this.vehicleTarget(options)
  }
 }

 let factory = new VehicleFactory()
 let instance1 = factory.createVehicle({
  type'car',
  color'yellow',
  doors4
 })
 
 let instance2 = factory.createVehicle({
  type'truck',
  state'new',
  wheelSize'small'
 })

尝试:

instance1 instanceof Car
instance2 instanceof Truck

总结说明

什么时候使用工厂模式

以下几种情景下工厂模式特别有用:

  • 对象的构建十分复杂
  • 需要依赖具体环境创建不同实例
  • 处理大量具有相同属性的小对象

什么时候不该用工厂模式

  • 不滥用运用工厂模式,有时候仅仅只是给代码增加了不必要的复杂度,同时使得测试难以运行下去。

最后

《前端面试题宝典》经过近一年的迭代,现已推出 小程序 和 电脑版刷题网站 (https://fe.ecool.fun/),欢迎大家使用~

同时,我们还推出了面试辅导的增值服务,可以为大家提供 “简历指导” 和 “模拟面试” 服务,感兴趣的同学可以联系小助手(微信号:interview-fe)进行报名。