Abstract Factory 抽象工厂模式

延续上一篇文章的例子,我们除了想要训练出 baseball player 之外,也希望他能够同时拥有一些装备,像是合适的棒球衣以及棒球鞋。

所以这里我们在 BaseballPlayer 当中加上 clothes 和 shoes 属性。同时为了举例,分别提供了一些细节

class BaseballPlayer {
  private name: string
  private clothes = {
    details: 'baseball clothes'
  }
  private shoes = {
    details: 'baseball clothes'
  }

  constructor(name: string){
    this.name = name
  }

  showEquipments(): void {
    console.log(`${this.name} has ${this.clothes.details} and ${this.shoes.details}`)
  }
}

实际执行结果如下

const jeter = new BaseballPlayer2('jeter')
jeter.showEquipments()  // jeter has baseball clothes and baseball shoes

这时 tennis player 同样也需要一套适合的运动衣和运动鞋,所以我们就可以复制上面的做法得到结果。

问题

但这时候就会思考,好像每个运动选手都需要运动衣与运动鞋,我们可以怎麽「集中管理」运动装备的生产,并依据不同运动员的需求,给予不同的实际产品呢?

实作步骤

步骤 1:先让我们把运动衣和运动鞋给抽象出来

abstract class Clothes {
  abstract details: string;
}

abstract class Shoes {
  abstract details: string;
}

步骤 2:建立专属於 baseball player 专属的运动衣和运动鞋类别

class BaseballClothes extends Clothes {
  constructor(){
    super()
  }

  details = 'baseball clothes'
}

class BaseballShoes extends Shoes {
  constructor(){
    super()
  }

  details = 'baseball shoes'
}

步骤 3:建立完「产品范本」之後,让我们开始建立工厂。首先同样先把我们想要的工厂的样子抽象出来。在这个 EquipmentFactory 当中,我希望他具备生产运动衣和运动鞋的方法

abstract class EquipmentFactory {
  abstract produceClothes(): Clothes;
  abstract produceShoes(): Shoes;
}

步骤 4:建立专门生产 baseball player 装备的工厂 BaseballEquipmentFactory 并放入实作产品的方法。

class BaseballEquipmentFactory extends EquipmentFactory {
  constructor(){
    super()
  }

  produceClothes(): Clothes {
    return new BaseballClothes()
  }

  produceShoes(): Shoes {
    return new BaseballShoes()
  }
}

步骤 5:最後,使用者可已透过 BaseballEquipmentFactory,来生产出我们期待中的产品

class BaseballPlayer {
  private name: string
  private baseballEquipmentFactory = new BaseballEquipmentFactory()
  private clothes = this.baseballEquipmentFactory.produceClothes()
  private shoes = this.baseballEquipmentFactory.produceShoes()

  constructor(name: string){
    this.name = name
  }

  showEquipments(): void {
    console.log(`${this.name} has ${this.clothes.details} and ${this.shoes.details}`)
  }
}

得到同样的结果

const jeter = new BaseballPlayer('jeter')
jeter.showEquipments()  // jeter has baseball clothes and baseball shoes

这里整理一下我们刚刚做的事情:

  1. 建立抽象产品 (Clothes, Shoes)
  2. 建立棒球选手产品的类别 (BaseballClothes, BaseballShoes)
  3. 建立抽象工厂 (EquipmentFactory)
  4. 建立棒球选手产品工厂 (BaseballEquipmentFactory)
  5. 使用者呼叫棒球选手产品工厂,得到想要的「产品组合」

当然,我们也可以为网球选手做同样的事情:

class TennisClothes extends Clothes {
  constructor(){
    super()
  }

  details = 'tennis clothes'
}

class TennisShoes extends Shoes {
  constructor(){
    super()
  }

  details = 'tennis shoes'
}

class TennisEquipmentFactory extends EquipmentFactory {
  constructor(){
    super()
  }

  produceClothes(): Clothes {
    return new TennisClothes()
  }

  produceShoes(): Shoes {
    return new TennisShoes()
  }
}

class TennisPlayer {
  tennisEquipmentFactory = new TennisEquipmentFactory()
  private name: string
  private clothes = this.tennisEquipmentFactory.produceClothes()
  private shoes = this.tennisEquipmentFactory.produceShoes()

  constructor(name: string){
    this.name = name
  }

  showEquipments(): void {
    console.log(`${this.name} has ${this.clothes.details} and ${this.shoes.details}`)
  }
}

得到结果

const federer = new TennisPlayer('federer')
federer.showEquipments()  // federer has tennis clothes and tennis shoes

抽象工厂模式

上面这样的模式,就是抽象工厂模式。这里要注意的是,不是用 abstract 宣告工厂类别就是抽象工厂。

抽象工厂的重点在於,能够帮助我们快速建立一系列的产品,譬如棒球衣和棒球鞋、网球衣和网球鞋。虽然这这些产品之前可能没有关系(衣服和鞋子),不过我们可以透过抽象工厂,将它们组合在一起

优点与缺点

抽象工厂模式的优点在於,能够让使用者只要透过一个工厂,就能够得到系列产品。另一方面,当我们定义好系列产品的样子之後,就可以扩张成不同系列,譬如从原本的棒球系列,拓展的篮球系列、足球系列等等。

但是缺点在於,如果我们要拓展系列本身,譬如从原本的「衣服 + 鞋子」,变成「衣服 + 鞋子 + 帽子」,那麽就会牵一发动全身,所有不同系列(棒球、网球、篮球 ... 等)的实作工厂都要同时变动。

使用场景

如果系列本身只有一个产品,譬如我只做衣服产品(棒球衣、网球衣、足球衣),那麽其实就跟原本的工厂模式一样,就不需要使用到抽象工厂模式。

另一方面,如果只有一种系列要做,譬如我只想做棒球系列(棒球衣、棒球鞋、棒球帽),那麽甚至我们可以直接使用简单工厂,让使用者传入参数(订单),然後直接产出他们需要的产品即可。不需要考虑到任何扩张的情境。


<<:  Angular 深入浅出三十天:表单与测试 Day19 - 与 Cypress 的初次见面(下)

>>:  Day19 - 写出更有品质的程序码,信 eslint 得永生

安全功能(security function)

. 根据NIST术语表,安全功能是指系统级别的“系统或系统元素提供的功能” 。 . 独立安全审核的...

用 Python 畅玩 Line bot - 23:Flask(一)

如果想要将从 line 上蒐集到的资料或数据呈现在自己的网页上,我们可以使用 flask 建立好网页...

#16. Quiz App(原生JS版)

#16. Quiz App 所谓Quiz App就是提供给用户答题的小应用,包含数个选择题,选完一个...

Python list 进阶

作天已经教大家列表的基础用法,今天要来教大家稍微金皆一点的,然後预告一下过几天可能就会进入到运用还有...

Day 28 JavaScript < 简单介绍>

1.JS是什麽? Java Script 是一种运行在客户端的脚本语言 (script就是脚本的意思...