iT邦幫忙

2021 iThome 鐵人賽

DAY 19
0

延續上一篇文章的例子,我們除了想要訓練出 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 宣告工廠類別就是抽象工廠。

抽象工廠的重點在於,能夠幫助我們快速建立一系列的產品,譬如棒球衣和棒球鞋、網球衣和網球鞋。雖然這這些產品之前可能沒有關係(衣服和鞋子),不過我們可以透過抽象工廠,將它們組合在一起

優點與缺點

抽象工廠模式的優點在於,能夠讓使用者只要透過一個工廠,就能夠得到系列產品。另一方面,當我們定義好系列產品的樣子之後,就可以擴張成不同系列,譬如從原本的棒球系列,拓展的籃球系列、足球系列等等。

但是缺點在於,如果我們要拓展系列本身,譬如從原本的「衣服 + 鞋子」,變成「衣服 + 鞋子 + 帽子」,那麼就會牽一髮動全身,所有不同系列(棒球、網球、籃球 ... 等)的實作工廠都要同時變動。

使用場景

如果系列本身只有一個產品,譬如我只做衣服產品(棒球衣、網球衣、足球衣),那麼其實就跟原本的工廠模式一樣,就不需要使用到抽象工廠模式。

另一方面,如果只有一種系列要做,譬如我只想做棒球系列(棒球衣、棒球鞋、棒球帽),那麼甚至我們可以直接使用簡單工廠,讓使用者傳入參數(訂單),然後直接產出他們需要的產品即可。不需要考慮到任何擴張的情境。


上一篇
Factory 工廠模式
下一篇
Builder 建造者模式
系列文
幫自己搞懂物件導向和設計模式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言