你的浏览器不支持canvas

Enjoy life!

javasript - 设计模式 - 工厂模式

Date: Author: JM

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。

一、简单工厂模式

1.1 介绍

  • 简单工厂模式(Simple Factory):也叫静态工厂方法,由一个工厂对象决定创建某一种产品对象类的实例。【主要用来创建同一类对象

1.2 例子

  • 先看下述代码:【demo
  /**
   * 警示框类
   * @param text
   * @constructor
   */
    const LoginAlert = function (text) {
      this.context = text
    }

    LoginAlert.prototype.show = function () {
      // 显示警示框
    }

   /**
   * 确认框类
   * @param text
   * @constructor
   */
    const LoginConfirm = function (text) {
      this.context = text
    }

    LoginConfirm.prototype.show = function () {
      // 显示确认框
    }

   /**
   * 提示框类
   * @param text
   * @constructor
   */
    const LoginPrompt = function (text) {
      this.context = text
    }

    LoginPrompt.prototype.show = function () {
      // 显示提示框框
    }
  • 大家会发现,这上面的类太多了,而且还是以Login为前缀,如果在注册模块也想用上述的代码,那么Login前缀就会显得怪怪的。
  • 除此之外,在每次创建时还要找到对应的类,实在太麻烦了!那么该如何解决呢?
  • 请看以下代码:【demo
/**
   * 简单工厂函数 - 自定义提示框 - 任意选择你想要的“框”
   * @param name
   * @param text
   * @returns {*}
   * @constructor
   */
    const PopFactory = function (name, text) {
      switch (name) {
        case 'alert':
          return new LoginAlert(text)
        case 'confirm':
          return new LoginConfirm(text)
        case 'prompt':
          return new LoginPrompt(text)
      }
    }
  • 上述代码体现了对不同类的实例化:你想要使用哪一种框,就将其类型名传进去上述函数就可了,就不用记着如此多复杂的东西。

1.3 相同地方抽取出来

  • 仔细一看,你会发现上述代码中有许多地方是一样的,例如:都有show方法和text,那么,就可以将他们抽取出来共用。
  • demo
 function createPop (type, text) {
      // 创建一个对象,并对对象拓展属性和方法
      const obj = new Object()
      obj.content = text
      // 显示方法
      obj.show = function () {}

      if (type === 'alert') {
        // 警示框差异部分
      }
      if (type === 'confirm') {
        // 确认框差异部分
      }
      if (type === 'prompt') {
        // 提示框差异部分
      }

      return obj
    }

    const userAlert = createPop('alert', '用户名只能是字母')

1.4 区别

  • 1.2 的简单工厂模式:是通过类实例化对象创建。
  • 1.3 的简单工厂模式:通过创建一个新对象然后包装增强其属性和功能实现。
  • 通过类创建的对象,如果这些类继承同一父类,那么他们的父类原型上的方法是可以共用

二、工厂方法模式

2.1 介绍

  • 工厂方法模式(Factory Method):通过对 产品类的抽象 使其创建业务主要 负责用于创建多类产品的实例。

2.2 例子

  • 开始做广告咯!
    • 第一批:Java,绿色字体
    • 第二批:PHP,黄色字体,红色背景
    • 第三批:JavaScript,粉色背景
  • 按照简单工厂模式,我们会按以下实现:【demo
    const Java = function (content) {}
    const Php = function (content) {}
    const JavaScrpt = function (content) {}
    
    // 工厂函数
    function JobFactory (type, content) {
      switch (type){
        case 'java':
          return new Java()
        // ,,,
      }
    }

  • 突然又来多了第四批:UI科学,红色边框,那岂不是又要做多两样东西:新建多一个类,修改工厂函数。
  • 每次多一批广告,都要做多两步,积少成多,那岂不是很耗时!因此,我们可以使用工厂方法模式! 【demo
 // 安全模式创建的工厂类
    const Factory = function (type, content) {
      // 安全模式类: 在构造函数开始时先判断当前对象this指代是不是类Factory
      // 如果是,则通过 new 关键字创建对象(这里是指Java、php);
      // 如果不是,说明类在全局作用域中执行,所以this指向window,因此就需要返回Factory的实例
      if (this instanceof Factory) {
        console.log(this instanceof Factory)
        const s = new this[type](content)
        return s
      } else {
        console.log(this instanceof Factory)
        return new Factory(type, content)
      }
    }

    // 工厂原型中设置创建所有类型数据对象的基类
    Factory.prototype = {
      Java: function (content) {
        // ...
      },
      JavaScript: function (content) {
        // ...
      },
      php: function (content) {
        // ...
      },
      UI: function (content) {
        // ...
      }
    }

    
    const data = [
      {
        type: 'Java',
        content: 'Java'
      },
      {
        type: 'php',
        content: 'php'
      },{
        type: 'JavaScript',
        content: 'JavaScript'
      }
      // ...
    ]
    
    for (let item of data) {
      Factory(item.type, item.content)
    }
  • 输出Factory,如下图:

factory

三、抽象工厂模式

3.1 介绍

  • 抽象工厂模式(Abstract Factory):通过对 类的工厂抽象 使其业务用于对产品类簇的创建,而不负责创建某一类产品的实例。

3.2 抽象类

  • 抽象类:Java中的抽象类是一种声明但不能使用的类,当你使用时就会报错。
  • 显然JavaScript是没有这抽象类,但我们可以手动抛出错误来模拟抽象类。
    const Car = function () {}
    Car.prototype = {
      constructor: Car,
      getPrice: function () {
        return new Error('抽象方法不能调用')
      },
      getSpeed: function () {
        return new Error('抽象方法不能调用')
      }
    }

    console.log(new Car().getPrice()) // 抽象方法不能调用

3.3 抽象工厂模式实例

  • 抽象工厂模式,在JavaScript中一般不会用来创建具体对象,原因如下:
    • 抽象类中定义的方法只是显性地定义一些功能,但没有具体的实现;
    • 而一个对象是要具有一套完整的功能
    • 所以用抽象类创建的对象当然也是“抽象”,所以我们不能使用它来创建一个真实的对象
  /**
   * 抽象工厂方法
   * @param subType
   * @param superType
   * @constructor
   */
   const VehicleFactory = function (subType, superType) {
     // 判断抽象工厂中是否有该抽象类
     if (Object.prototype.toString.call(VehicleFactory[superType]) === '[object Function]') {
       // 缓存类
       function F () {}
       // 继承父类属性和方法
       F.prototype = new VehicleFactory[superType]()
       // 将子类constructor指向子类
       subType.constructor = subType
       // 子类原型继承父类
       subType.prototype = new F()
     } else {
       // 不存在该抽象类抛出错误
       throw new Error('未创建该抽象类')
     }
   }

  /**
   * 小汽车抽象类
   * @constructor
   */
   VehicleFactory.Car = function () {
     this.type = 'car'
   }
   VehicleFactory.Car.prototype = {
     getPrice: function () {
       return new Error('抽象方法不能调用')
     },
     getSpeed: function () {
       return new Error('抽象方法不能调用')
     }
   }

  /**
   * 汽车子类:宝马
   * @param price
   * @param speed
   * @constructor
   */
   const BMW = function (price, speed) {
     this.price = price
     this.speed = speed
   }
   VehicleFactory(BMW, 'Car')
   BMW.prototype.getPrice = function () {
     return this.price
   }
//  BMW.prototype.getSpeed = function () {
//    return this.speed
//  }

  const bmw = new BMW(12, 2)
  console.log(bmw.getPrice()) // 12
  console.log(bmw.getSpeed()) // 抽象方法不能调用

四、总结

  • 简单工厂模式:适用于创建少类对象
  • 工厂方法模式:适用于创建多类对象,同时也避免了使用者与对象类之间的耦合,用户不必关心创建该对象的具体类,只需要调用工厂方法即可。
    • 这里需提醒一下:避免因错误调用,让this对象变为全局对象,这里就需用到安全模式创建的工厂对象
  • 抽象工厂模式:创建出来的结果不是一个真实的对象实例,而是一个类簇,它制定了类的结构。

对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。