從零開始學(xué)設(shè)計(jì)模式(二)——抽象工廠模式

抽象工廠模式(Abstract Factory)

抽象工廠模式可以說是對簡單工廠模式的一種延伸,它是圍繞一個(gè)超級工廠來創(chuàng)建其他簡單工廠,該超級工廠又稱為其他工廠的工廠。

這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。

在抽象工廠模式中,接口是負(fù)責(zé)創(chuàng)建一個(gè)相關(guān)對象的工廠,不需要顯式指定它們的類,每個(gè)生成的工廠都能按照工廠模式提供對象。

意圖

提供一個(gè)接口,用于創(chuàng)建一系列相關(guān)或有依賴關(guān)系的對象,而無需指定它們的具體類。

主要解決:主要解決接口選擇的問題。

何時(shí)使用:系統(tǒng)的產(chǎn)品有多于一個(gè)的產(chǎn)品族類,而系統(tǒng)只消費(fèi)其中某一族類的產(chǎn)品。

如何解決:在一個(gè)產(chǎn)品族里面,定義多個(gè)產(chǎn)品。

關(guān)鍵代碼:在一個(gè)工廠里聚合多個(gè)同類產(chǎn)品。

解釋

現(xiàn)實(shí)世界的例子

要?jiǎng)?chuàng)建一個(gè)王國,我們需要有共同主題的物體。精靈王國需要精靈國王、精靈城堡和精靈軍隊(duì),而獸人王國需要獸人國王、獸人城堡和獸人軍隊(duì)。王國中的對象之間存在依賴關(guān)系

簡而言之

工廠的工廠;將個(gè)別但相關(guān)/從屬工廠組合在一起而不指定其具體類別的工廠。

維基百科

The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes(抽象工廠模式提供了一種封裝一組具有共同主題的獨(dú)立工廠的方法,而無需指定它們的具體類)

程序代碼實(shí)現(xiàn)

以上面現(xiàn)實(shí)世界的王國例子。下面的類圖展示了不同具體的工廠和它們生產(chǎn)的不同具體產(chǎn)品,精靈王國的工廠生產(chǎn)精靈城堡和精靈軍隊(duì),獸人王國的工廠生產(chǎn)獸人城堡和獸人軍隊(duì)。

diagram_kingdom1.png

按照上面的類圖,我們首先來定義一些接口和實(shí)現(xiàn)類在王國中:

public interface Castle {
  String getDescription();
}
public interface King {
  String getDescription();
}
public interface Army {
  String getDescription();
}

// Elven implementations ->
public class ElfCastle implements Castle {
  static final String DESCRIPTION = "This is the Elven castle!";
  @Override
  public String getDescription() {
    return DESCRIPTION;
  }
}
public class ElfKing implements King {
  static final String DESCRIPTION = "This is the Elven king!";
  @Override
  public String getDescription() {
    return DESCRIPTION;
  }
}
public class ElfArmy implements Army {
  static final String DESCRIPTION = "This is the Elven Army!";
  @Override
  public String getDescription() {
    return DESCRIPTION;
  }
}

// Orcish implementations similarly... 獸人實(shí)現(xiàn)與上面類似

接下來我們再來定義抽象的王國工廠接口和他的具體實(shí)現(xiàn)工廠

public interface KingdomFactory {
  Castle createCastle();
  King createKing();
  Army createArmy();
}

public class ElfKingdomFactory implements KingdomFactory {
  public Castle createCastle() {
    return new ElfCastle();
  }
  public King createKing() {
    return new ElfKing();
  }
  public Army createArmy() {
    return new ElfArmy();
  }
}

public class OrcKingdomFactory implements KingdomFactory {
  public Castle createCastle() {
    return new OrcCastle();
  }
  public King createKing() {
    return new OrcKing();
  }
  public Army createArmy() {
    return new OrcArmy();
  }
}

現(xiàn)在我們有了我們的抽象工廠,讓我們制作相關(guān)聯(lián)類別的家族對象,例如精靈王國工廠創(chuàng)建精靈城堡、精靈國王和精靈軍隊(duì)等

KingdomFactory factory = new ElfKingdomFactory();
Castle castle = factory.createCastle();
King king = factory.createKing();
Army army = factory.createArmy();

castle.getDescription();  // Output: This is the Elven castle!
king.getDescription(); // Output: This is the Elven king!
army.getDescription(); // Output: This is the Elven Army!

最后我們再設(shè)計(jì)一個(gè)超級工廠來生產(chǎn)不同種類的王國工廠。在本例中我們創(chuàng)建一個(gè)FactoryMaker來生產(chǎn)ElfKingdomFactory or OrcKingdomFactory

客戶端可以通過FactoryMaker類來創(chuàng)建期望的具體王國工廠,具體的工廠將會生產(chǎn)具體的對象(軍隊(duì)、國王、城堡)

同樣在本例中我們客戶端也使用枚舉作為參數(shù)來指明我們想創(chuàng)建具體哪一類的工廠。這一塊UML關(guān)系class類圖如下:

diagram_kingdom2

部分重要代碼如下:

public static class FactoryMaker {

  public enum KingdomType {
    ELF, ORC
  }

  public static KingdomFactory makeFactory(KingdomType type) {
    switch (type) {
      case ELF:
        return new ElfKingdomFactory();
      case ORC:
        return new OrcKingdomFactory();
      default:
        throw new IllegalArgumentException("KingdomType not supported.");
    }
  }
}

public static void main(String[] args) {
  
    App app = new App();

    LOGGER.info("Elf Kingdom");
    app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF));
    LOGGER.info(app.getArmy().getDescription());
    LOGGER.info(app.getCastle().getDescription());
    LOGGER.info(app.getKing().getDescription());

    LOGGER.info("Orc Kingdom");
    app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC));
    LOGGER.info(app.getArmy().getDescription());
    LOGGER.info(app.getCastle().getDescription());
    LOGGER.info(app.getKing().getDescription());
  }

運(yùn)行程序app輸出:

16:06:12.289 [main] INFO com.iluwatar.abstractfactory.App - Elf Kingdom
16:06:12.295 [main] INFO com.iluwatar.abstractfactory.App - This is the Elven Army!
16:06:12.295 [main] INFO com.iluwatar.abstractfactory.App - This is the Elven castle!
16:06:12.295 [main] INFO com.iluwatar.abstractfactory.App - This is the Elven king!
16:06:12.296 [main] INFO com.iluwatar.abstractfactory.App - Orc Kingdom
16:06:12.297 [main] INFO com.iluwatar.abstractfactory.App - This is the Orc Army!
16:06:12.297 [main] INFO com.iluwatar.abstractfactory.App - This is the Orc castle!
16:06:12.297 [main] INFO com.iluwatar.abstractfactory.App - This is the Orc king!

應(yīng)用場景

當(dāng)遇到如下情形時(shí),你應(yīng)該考慮使用抽象工廠模式:

  1. 一個(gè)系統(tǒng)應(yīng)該獨(dú)立于它的產(chǎn)品是如何創(chuàng)建、組成和表現(xiàn)的
  2. 系統(tǒng)應(yīng)該配置有多個(gè)產(chǎn)品系列之一
  3. 相關(guān)產(chǎn)品對象系列被設(shè)計(jì)為一起使用,你需要強(qiáng)制執(zhí)行此約束
  4. 你想提供一個(gè)產(chǎn)品類庫,你想展示的只是他們的接口,而不是他們的實(shí)現(xiàn)
  5. 依賴項(xiàng)的生命周期在概念上比消費(fèi)者的生命周期短
  6. 你需要一個(gè)運(yùn)行時(shí)值來構(gòu)造一個(gè)特定的依賴關(guān)系
  7. 你想決定在運(yùn)行時(shí)從一個(gè)家族中調(diào)用哪個(gè)產(chǎn)品
  8. 你需要通過提供一個(gè)或多個(gè)僅在運(yùn)行時(shí)已知的參數(shù),就能來解析依賴項(xiàng)

使用場景

  • 應(yīng)用在運(yùn)行時(shí)需要選擇調(diào)用適當(dāng)?shù)膶?shí)現(xiàn)如FileSystemAcmeService or DatabaseAcmeService or NetworkAcmeService
  • 單元測試用例編寫變的更加簡單

Java中的現(xiàn)實(shí)例子

優(yōu)缺點(diǎn)

優(yōu)點(diǎn):當(dāng)一個(gè)產(chǎn)品族中的多個(gè)對象被設(shè)計(jì)成一起工作時(shí),它能保證客戶端始終只使用同一個(gè)產(chǎn)品族中的對象。

缺點(diǎn):產(chǎn)品族擴(kuò)展非常困難,要增加一個(gè)系列的某一產(chǎn)品,既要在抽象的 Creator 里加代碼,又要在具體的里面加代碼

寫在最后

抽象工廠模式是工廠模式的超級聚合,當(dāng)產(chǎn)品只有一個(gè)的時(shí)候,抽象工廠模式也就變成了工廠模式,當(dāng)工廠模式的產(chǎn)品變成多個(gè)時(shí),工廠模式也就變成了抽象工廠模式。

抽象工廠的模式比工廠模式要復(fù)雜很多,這也就導(dǎo)致系統(tǒng)在擴(kuò)展一個(gè)工廠或者一個(gè)產(chǎn)品時(shí),需要改動(dòng)的地方會很多,程序耦合性較高。

以上面王國的例子,假如我們要新增一個(gè)人類王國,那么我們需要新增人類王國工廠,人類城堡、人類軍隊(duì),UML類圖如下:


diagram_humanKingdom.png

同樣再想想,假如我們要新增一個(gè)產(chǎn)品的時(shí)候,如一個(gè)王國除了國王、城堡、軍隊(duì)之外還應(yīng)該有瞭望塔,那么在上圖的基礎(chǔ)上我們需要修改和新增哪些接口和類呢?讀者可以自己思考一下,試著畫一下UML圖。

下一章節(jié)我將書寫單例模式
碼字不易,各位看官喜歡的話,請給點(diǎn)個(gè)贊 ??,我將持續(xù)更新,謝謝!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容