延續上一篇文章的例子,我們除了想要訓練出 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
這裡整理一下我們剛剛做的事情:
當然,我們也可以為網球選手做同樣的事情:
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 宣告工廠類別就是抽象工廠。
抽象工廠的重點在於,能夠幫助我們快速建立一系列的產品,譬如棒球衣和棒球鞋、網球衣和網球鞋。雖然這這些產品之前可能沒有關係(衣服和鞋子),不過我們可以透過抽象工廠,將它們組合在一起
抽象工廠模式的優點在於,能夠讓使用者只要透過一個工廠,就能夠得到系列產品。另一方面,當我們定義好系列產品的樣子之後,就可以擴張成不同系列,譬如從原本的棒球系列,拓展的籃球系列、足球系列等等。
但是缺點在於,如果我們要拓展系列本身,譬如從原本的「衣服 + 鞋子」,變成「衣服 + 鞋子 + 帽子」,那麼就會牽一髮動全身,所有不同系列(棒球、網球、籃球 ... 等)的實作工廠都要同時變動。
如果系列本身只有一個產品,譬如我只做衣服產品(棒球衣、網球衣、足球衣),那麼其實就跟原本的工廠模式一樣,就不需要使用到抽象工廠模式。
另一方面,如果只有一種系列要做,譬如我只想做棒球系列(棒球衣、棒球鞋、棒球帽),那麼甚至我們可以直接使用簡單工廠,讓使用者傳入參數(訂單),然後直接產出他們需要的產品即可。不需要考慮到任何擴張的情境。