前言

这是关于设计模式的系列文章,在每篇文章中将对常见设计模式进行讲解,因为针对前端方向,而且前端常用语言 JavaScript 本身是弱类型,面向对象(模拟面向对象)编程的实现相较于其他强类型语言实现更为繁琐,所以代码主要以 JavaScript 表现。

系列文章链接:

简单工厂模式

“简单工厂模式” 是由一个工厂对象决定创建出哪一种产品类的实例。


简单工厂模式 UML 图
简单工厂模式 UML 图


/* 直接创建子类实例 */
// 父类
class Plant {
  constructor(name) {
    this.name = name;
  }
  grow() {
    console.log('I am growing!');
  }
}

// 子类 —— Apple
class Apple extends Plant {
  constructor(name, flavour) {
    super(name);
    this.flavour = flavour;
  }
}

// 子类 —— Orange
class Orange extends Plant {
  constructor(name, flavour) {
    super(name);
    this.flavour = flavour;
  }
}

// 直接创建子类的实例
const apple = new Apple('苹果', '甜');
const orange = new Orange('橘子', '酸');

console.log(apple.flavour); // 甜
console.log(orange.flavour); // 酸

上面创建子类实例的方式是使用 new 关键字直接创建,这种创建方式使产生的实例和具体的类紧紧的耦合在一起,并依赖于类的具体实现,如果在子类可能随时发生变化的代码中,将对维护造成麻烦,使用 “简单工厂模式” 可以对产生的实例和具体的类进行解耦,且不必关心子类的具体实现和在未来是否发生变化。

/* 使用简单工厂模式创建子类的实例 */
// 父类
class Plant {
  constructor(name) {
    this.name = name;
  }
  grow() {
    console.log('I am growing!');
  }
}

// 子类 —— Apple
class Apple extends Plant {
  constructor(name, flavour) {
    super(name);
    this.flavour = flavour;
  }
}

// 子类 —— Orange
class Orange extends Plant {
  constructor(name, flavour) {
    super(name);
    this.flavour = flavour;
  }
}

// 工厂类
class Factory {
  static create(type) {
    switch (type) {
      case 'apple':
        return new Apple('苹果', '甜');
      case 'orange':
        return new Orange('桔子', '酸');
      default:
        throw new Error('no constructor!');
    }
  }
}

// 使用简单工厂创建子类实例
const apple = Factory.create('apple');
const orange = Factory.create('orange');

console.log(apple.flavour); // 甜
console.log(orange.flavour); // 酸

从上面代码看,我们只需要通过类型就可以得到某一个子类的实例,不需要知道子类是谁,以及具体实现,并在工厂 Factory 中做了错误处理,可以不必担心未来某一个子类发生变化或者被删除的问题。

/* 简单工厂经典案例 —— jQuery */
// jQuery 的构造函数
class JQuery {
  constructor (selector) {
    this.selector = selector;
    const elements = document.querySelectorAll(selector);
    this.length = elements.length;

    for (let i = 0; i < this.length; i++) {
      this[i] = elements[i];
    }
  }
  html() {
    return this[0].innerHTML;
  }
}

// 简单工厂函数
window.$ = function (selector) {
  return new JQuery(selector);
}

// 获取 li 标签并调用 html 方法
const html = $('li').html();
/* 简单工厂经典案例 —— React 虚拟 DOM */
// 创建虚拟 DOM 的构造函数
class VNode {
  constructor(tagName, attrs, children) {
    this.tagName = tagName;
    this.attrs = attrs;
    this.children = children;
  }
}

// 挂在 React 对象上的简单工厂函数
React.createElement = function (tagName, attrs, children) {
  return new VNode(tagName, attrs, children);
}

简单工厂模式的缺点:不满足开放封闭原则,内部可以随意修改,新增、修改子类都需要修改工厂类内部代码,在扩展的过程中工厂类的代码将会越来越臃肿。

工厂方法模式

“工厂方法模式” 可以规避 “简单工厂模式” 的缺点,又称为 “多态性工厂模式”,核心的工厂类不再负责创建出哪一种产品类的实例,而是将具体创建的工作交给子类去做。


工厂方法模式 UML 图
工厂方法模式 UML 图


/* 基本的工厂方法模式使用 */
// 父类
class Plant {
  constructor(name) {
    this.name = name;
  }
}

// 子类 —— Apple
class Apple extends Plant {
  constructor (name, flavour) {
    super(name);
    this.flavour = flavour;
  }
}

// 子类 —— Orange
class Orange extends Plant {
  constructor (name, flavour) {
    super(name);
    this.flavour = flavour;
  }
}

// 父类工厂(提供工厂类所共有的内容,依赖倒置原则,依赖抽象而不依赖实现)
class Factory {
  create () {}
}

// 子类工厂 —— AppleFactory
class AppleFactory extends Factory {
  static create() {
    return new Apple('苹果', '甜');
  }
}

// 子类工厂 —— OrangeFactory
class OrangeFactory extends Factory {
  static create() {
    return new Orange('桔子', '酸');
  }
}

// 创建实例
const apple = AppleFactory.create();
const orange = OrangeFactory.create();

console.log(apple.flavour); // 甜
console.log(orange.flavour); // 酸

上面是一个基础的 “工厂方法模式” 使用,解决了 “简单工厂模式” 扩展的问题(遵循开放封闭原则),创建实例虽然不耦合具体的类,但是耦合工厂的子类,下面可以通过文件拆分进行解耦。

/* plant.js */
// 父类
class Plant {
  constructor(name) {
    this.name = name;
  }
}

module.exports = Plant;
/* factory.js */
// 父类工厂(提供工厂类所共有的内容,依赖倒置原则,依赖抽象而不依赖实现)
class Factory {
  create () {}
}

module.exports = Factory;
/* apple.js */
const Plant = require('./plant');
const Factory = require('./factory');

// 子类 —— Apple
class Apple extends Plant {
  constructor (name, flavour) {
    super(name);
    this.flavour = flavour;
  }
}

// 子类工厂 —— AppleFactory
class AppleFactory extends Factory {
  static create() {
    return new Apple('苹果', '甜');
  }
}

module.exports = AppleFactory;
/* orange.js */
const Plant = require('./plant');
const Factory = require('./factory');

// 子类 —— Orange
class Orange extends Plant {
  constructor (name, flavour) {
    super(name);
    this.flavour = flavour;
  }
}

// 子类工厂 —— OrangeFactory
class OrangeFactory extends Factory {
  static create() {
    return new Orange('桔子', '酸');
  }
}

module.exports = OrangeFactory;
/* setting.js */
// 配置文件,将要创建实例的类型与对应的工厂关联起来
const setting = {
  apple: './apple',
  orange: './orange',
};

module.exports = setting;
/* use.js */
const setting = require('./setting');

const apple = require(setting['apple']).create();
const orange = require(setting['orange']).create();

console.log(apple.flavour); // 甜
console.log(orange.flavour); // 酸

使用上面这样的 “工厂方法模式”,扩展时只需要新增一个文件,在文件中定义具体创建实例的类和工厂类就可以了,一般会有一个配置文件将要创建实例的类型和对应的工厂关联起来,创建对应的实例只需通过类型和配置文件找到对应的工厂执行 create 方法即可。

抽象工厂模式

“抽象工厂模式” 是指当有多个抽象角色时,可以提供一个接口,不必指定 “产品” 具体的情况下,创建多个产品族中的产品对象。


抽象工厂模式 UML 图
抽象工厂模式 UML 图


// 父类 —— Icon
class Icon {
  render() {}
}

// 父类 —— Button
class Button {
  render() {}
}

// 子类 —— AppleIcon 苹果图标
class AppleIcon extends Icon {
  render() {
    console.log('绘制 Mac 的图标');
  }
}

// 子类 —— AppleButton 苹果按钮
class AppleButton extends Button {
  render() {
    console.log('绘制 Mac 的按钮');
  }
}

// 子类 —— WindowsIcon Windows 图标
class WindowsIcon extends Icon {
  render() {
    console.log('绘制 Windows 的图标');
  }
}

// 子类 —— WindowsButton Windows 按钮
class WindowsButton extends Button {
  render() {
    console.log('绘制 Windows 的按钮');
  }
}

// 父类工厂
class Factory {
  createIcon() {} // 创建图标
  createButton() {} // 创建按钮
}

// 子类工厂 —— AppleFactory 用于创建苹果族产品实例
class AppleFactory extends Factory {
  createIcon() {
    return new AppleIcon();
  }
  createButton() {
    return new AppleButton();
  }
}

// 子类工厂 —— WindowsFactory 用于创建 Windows 族产品实例
class WindowsFactory extends Factory {
  createIcon() {
    return new WindowsIcon();
  }
  createButton() {
    return new WindowsButton();
  }
}

// 创建苹果工厂实例
const appleFactory = new AppleFactory();

// 创建苹果族产品
appleFactory.createIcon().render(); // 绘制 Mac 的图标
appleFactory.createButton().render(); // 绘制 Mac 的按钮

// 创建 Windows 工厂实例
const wondowsFactory = new WindowsFactory();

// 创建 Windows 族产品
wondowsFactory.createIcon().render(); // 绘制 Windows 的图标
wondowsFactory.createButton().render(); // 绘制 Windows 的按钮

在上面案例中,按照 “抽象工厂模式” 的说法,多个抽象角色指的是 AppleWindowsIconButton,工厂分为 AppleFactoryWindowsFactory 两类,可以分别创建对应产品的 IconButton 实例。

总结

上面几种工厂模式中,“简单工厂模式” 在框架开发中使用居多,“工厂方法模式” 更多在一些比较老的且复杂的项目中用作业务模块封装和抽象,“抽象工厂模式” 在前端并不常用,应用于后端偏多,最后附上 案例地址