设计模式 - js 工厂模式 之 工厂方法模式

454 阅读4分钟

「这是我参与2022首次更文挑战的第18天,活动详情查看:2022首次更文挑战

在学习中有这样一段话:在学一样东西的时候,可以按照它是什么,有什么用,优点是什么,缺点是什么,要怎么去使用,或者使用的时候要注意什么。我觉得这五部曲还是很有用的,能帮你快速掌握一个知识点

前言

昨天我们介绍了简单工厂模式,提到了简单工厂模式存在的问题:

  • 违背了 开闭原则 ,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂。
  • 简单工厂模式由于使用了 static 方法,不能被继承和重写,这样就没有办法通过继承来形成一个阶级结构。

工厂方法模式的出现,就是为了解决这个问题,对简单工厂模式进一步抽象化,使其可以在不修改原来代码的情况下引进新的产品,也就是满足了开闭原则。

工厂方法模式是什么

工厂方法模式,又称工厂模式、多态工厂模式和虚拟构造器模式,通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。

简单的来说,就是将类的实例化也就是之前的创建产品,不再交给工厂类来实现,而是定义子类来决定实例化哪一个类,也就是创建哪种产品。

将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定应该实例化(创建)哪一个类。

工厂方法模式的优点

  • 灵活性增强 。对于添加新产品的过程就是新增工厂子类而不是修改工厂逻辑,符合 开闭原则
  • 解耦 。不再是由工厂类负责所以产品的创建,而是由工厂给出接口,子类来进行创建。

工厂方法模式的缺点

  • 系统复杂度提升。类的个数容易过多,增加了系统的复杂程度。
  • 只能产生一种品种的产品。抽象类只能产生一种类型的产品,多个类型将会有多个抽象类,这个弊端会在 抽象工厂模式 解决。

工厂方法模式的使用

工厂方法模式的本意是将实际创建对象的工作推迟到子类中,这样核心类就变成了抽象类。但是在js中并不存在抽象类的创建方式,所以我们只能是模仿核心思想。并且在实例化的过程当中,我们可以通过 new.target 来防止工厂类被实例化。

new.target - JavaScript | MDN (mozilla.org)

class Card {
  constructor(opt) {
    if (new.target === Card) {
      throw new Error("抽象类不能被实例化");
    }
    this.name = (opt && opt.name) || "";
    this.calculate = (opt && opt.calculate) || null;
  }
}
class CardFactory extends Card {
  constructor(name, calculate) {
    super(name, calculate);
  }
  getInstance(role) {
    switch (role) {
      case "common":
        return new CardFactory({
          name: "普通会员",
          calculate: (price) => {
            return price * 0.9;
          },
        });
        break;
      case "sliver":
        return new CardFactory({
          name: "白银会员",
          calculate: (price) => {
            return price * 0.8;
          },
        });
        break;
      case "glod":
        return new CardFactory({
          name: "黄金会员",
          calculate: (price) => {
            return price * 0.7;
          },
        });
        break;
      default:
        throw new Error("参数错误, 可选参数:common, sliver, glod");
    }
  }
}

let cardFactory = new CardFactory();

let common = cardFactory.getInstance("common");
console.log(common.calculate(100));

let sliver = cardFactory.getInstance("sliver");
console.log(sliver.calculate(100));

let glod = cardFactory.getInstance("glod");
console.log(glod.calculate(100));

但是要注意,最开始创建工厂方法的时候,是没有存入对应的参数的,所以在父类中要做对应的判断防止报错。剩下的就是和简单工厂模式差不多的使用,不同的实例会调用他们自己的方法返回不同的结果。

以上就是一个简单的工厂方法模式,可以看出来比起之前的简单工厂模式,工厂方法模式将这一过程进行了相对的解耦,更加符合设计原则。

总结

工厂方法模式 解决了 简单工厂模式 带来的部分问题,但是也存在着部分问题,在之后会有 抽象工厂模式 来解决 工厂方法模式 的部分缺点,后续会继续降到它是如果解决的,做出了什么样子的优化。