JavaScript設計模式第3篇:抽象工廠模式

  • 2019 年 12 月 16 日
  • 筆記

接著上一篇《JavaScript設計模式第2篇:工廠模式》,今天我們來看工廠模式的最後一種:抽象工廠

定義

有了前一節工廠方法模式的基礎,抽象工廠其實很類似,只不過工廠方法針對的是一個產品等級結構,而抽象工廠針對的是多個產品等級結構。

我們先來解釋 2 個概念:產品等級結構產品族

產品等級結構:

  • 產品等級結構就是產品的繼承結構,比如一個抽象類是電視機,那麼其子類會有海爾電視,TCL電視,小米電視等等,那麼抽象電視機和具體品牌的電視機之間就構成了一個產品等級結構,抽象電視機是父類,具體品牌的電視機是子類。

產品族:

  • 在抽象工廠模式中,產品族是指由一個工廠生產的,位於不同產品等級結構中的一組產品,比如海爾電器工廠既生產海爾電視機,也生產海爾熱水器,電視機和熱水器位於不同的產品等級結構中,如果它們是由同一個工廠生產的,就稱為產品族。

抽象工廠模式,官方定義,提供一個創建一系列相關或相互依賴對象的介面,而無須指定它們具體的類。

抽象工廠模式包含如下 4 種角色:

  • 抽象工廠
  • 具體工廠
  • 抽象產品
  • 具體產品

概念還是太生澀了,下面看例子。

例子

上一節我們的汽車廠商只能生產 Car,但是我們知道,很多汽車廠商也可以生產發動機 Engine,這就是兩個產品等級結構,一個產品族。

我們先定義一個汽車廠商的抽象工廠 automakerFactory,提供 2 個抽象方法 createCar 和 createEngine:

class AutomakerFactory {      createCar () {          throw new Error('不能調用抽象方法,請自己實現');      }        createEngine () {          throw new Error('不能調用抽象方法,請自己實現');      }  }  複製程式碼

然後定義具體工廠實現抽象工廠:

class BenzFactory extends AutomakerFactory {      createCar () {          return new BenzCar();      }        createEngine () {          return new BenzEngine();      }  }    class AudiFactory extends AutomakerFactory {      createCar () {          return new AudiCar();      }        createEngine () {          return new AudiEngine();      }  }  複製程式碼

定義抽象產品類 Car 和 具體產品類 BenzCar、AudiCar:

class Car {      drive () {          throw new Error('不能調用抽象方法,請自己實現');      }  }    class BenzCar extends Car {      drive () {          console.log('Benz drive');      }  }    class AudiCar extends Car {      drive () {          console.log('Audi drive');      }  }  複製程式碼

定義抽象產品類 Engine 和 具體產品類 BenzEngine、AudiEngine:

class Engine {      start () {          throw new Error('不能調用抽象方法,請自己實現');      }  }    class BenzEngine extends Engine {      start () {          console.log('Benz engine start');      }  }    class AudiEngine extends Engine {      start () {          console.log('Audi engine start');      }  }  複製程式碼

如上,抽象工廠需要的 4 種角色就創建完成了,我們來看一下實例化過程:

let benz = new BenzFactory();  let benzCar = benz.createCar();  let benzEngine = benz.createEngine();    let audi = new AudiFactory();  let audiCar = audi.createCar();  let audiEngine = audi.createEngine();    benzCar.drive();            // Benz drive  benzEngine.start();         // Benz engine start    audiCar.drive();            // Audi drive  audiEngine.start();         // Audi engine start  

還是通過 UML 類圖來加深一下理解:

通過抽象工廠我們解決了什麼問題呢?

假設現在需要增加另一個廠商 BMW,我們需要做些什麼:

  1. 新增 BMW 具體工廠類
class BMWFactory extends AutomakerFactory {      createCar () {          return new BMWCar();      }        createEngine () {          return new BMWEngine();      }  }  複製程式碼
  1. 新增 BMW 具體產品類
class BMWCar extends Car {      drive () {          console.log('BMW drive');      }  }    class BMWEngine extends Engine {      start () {          console.log('BMW engine start');      }  }  複製程式碼
  1. 實例化
let bmw = new BMWFactory();  let bmwCar = bmw.createCar();  let bmwEngine = bmw.createEngine();    bmwCar.drive();            // BMW drive  bmwEngine.start();         // BMW engine start  

可以看到,和工廠方法一樣,也不需要修改已有工廠類,只需要新增自己的具體工廠類和具體產品類就可以了,也符合「開閉原則」,只不過和工廠方法不同的是,抽象工廠針對的是一類產品族中的不同產品等級結構這種場景,而工廠方法只是針對於一種產品等級結構的場景。

當然它的缺點也和工廠方法一樣,不斷的添加新產品會導致類越來越多,增加了系統複雜度。

總結

  1. 抽象工廠模式提供一個創建一系列相關或相互依賴對象的介面,而無須指定它們具體的類。抽象工廠模式又稱為Kit模式,屬於對象創建型模式
  2. 抽象工廠模式包含 4 種角色:
    • 抽象工廠、具體工廠、抽象產品、具體產品
    • 抽象工廠用於聲明生成抽象產品的方法
    • 具體工廠實現了抽象工廠聲明的生成抽象產品的方法,生成一組具體產品,這些產品構成了一個產品族,每一個產品都位於某個產品等級結構中
    • 抽象產品為每種產品聲明介面,在抽象產品中定義了產品的抽象業務方法
    • 具體產品定義具體工廠生產的具體產品對象,實現抽象產品介面中定義的業務方法
  3. 抽象工廠模式與工廠方法模式最大的區別在於,工廠方法模式針對的是一個產品等級結構,而抽象工廠模式則需要面對多個產品等級結構
  4. 抽象工廠模式的主要優點是隔離了具體類的生成,使得客戶並不需要知道什麼被創建,而且每次可以通過具體工廠類創建一個產品族中的多個對象,增加或者替換產品族比較方便,增加新的具體工廠和產品族很方便;主要缺點在於增加新的產品等級結構很複雜,需要修改抽象工廠和所有的具體工廠類,導致系統複雜度增加
  5. 抽象工廠模式適用情況包括:一個系統不應當依賴於產品類實例如何被創建、組合和表達的細節;系統中有多於一個的產品族,而每次只使用其中某一產品族;屬於同一個產品族的產品將在一起使用;系統提供一個產品類的庫,所有的產品以同樣的介面出現,從而使客戶端不依賴於具體實現