---
# System prepended metadata

title: Abstract Factory 抽象工廠 模式
tags: [基礎進階, Java 設計模式]

---

---
title: 'Abstract Factory 抽象工廠 模式'
disqus: kyleAlien
---

Abstract Factory 抽象工廠 模式
===

## Overview of Content

如有引用參考請詳註出處，感謝 :smile:

> [**Factory 工廠方法**](https://hackmd.io/40ZKAzDHTF6PMpbez-3vNg?view) 的升級版，抽象工廠多了依賴介面，**多型拓展並依賴其抽象**

:::success
* 如果喜歡讀更好看一點的網頁版本，可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/)
    
    本篇文章對應的是 [**Abstract Factory 設計模式 | 實現解說 | Android MediaPlayer**](https://devtechascendancy.com/object-oriented_design_abstract-factory/)
:::


[TOC]

## Abstract Factory 使用場景
一個 **物件集合有 ++相同約束(功能)++，但實作方法不同時**，即可使用

> 就像是 Window 系統、Mac 系統 都有藍芽功能（Factory），但掃描、連接、傳輸...實做都不同（Product），這就可以使用抽象工廠模式


### Abstract Factory 定義 & UML

* **`Abstract Factory` 定義**：
    
    **為創建一組 相關 或 相互依賴 的對象 提供一個抽象接口**，而 **不指定它們的具體類**

    :::success
    * **工廠模式 vs 抽象工廠模式**

        * **相同點**

            工廠模式、抽象工廠的建構對象都在內部，**使用者不用關心實際對象，而是關注界面（契約）**

        * **不同點**：**兩者對於生產的目的有明顯的不同**

            工廠模式中，使用者會關注 `Factory` 產生的 `Product`，產品與工廠的關係不大
            
            > 工廠模式生產 **一個產品**，這個產品有契約上定義的行為操作，但產品有不同特色（實作）

            **抽象工廠則是 `Product` 與 `Factory` 有一定的關聯性 (關係更重)，使用不同的 `Product` 會造出不同產品**

            > 抽象工廠生產多個產品（++**一系列產品**++），一組契約等著被實現，而各個產品也可以有不同特點
        
        * 抽象工廠手法的不同點
            
            抽象工廠將工廠主體類別中的抽象產品「提煉為一個產品界面」，用物件合成（Object Composition） 取代了類別繼承，所以會更加的靈活，也更加的抽象、複雜
    :::
    
* 抽象工廠 UML：抽象工廠的 **重點是在抽象 Factory 上依賴其他抽象 Product**，透過操控工廠產生不同產品

    | 類 | 功能 |
    | -------- | -------- |
    | `IFactory` (抽象) | 依賴抽象 Product、所以產生的產品也是抽象 |
    | `ConcreateFactory_A、B` | 實做工廠類，藉由抽象產品，創建目標類 |
    | `IProductA、B` (抽象) | 抽象產品，也就是各個產品的行為契約 |
    | `ConcreateProdcut_A1`、`A2`、`B1`、`B2` | 定義抽象產品的細節 |
    
    > ![](https://i.imgur.com/gDTy7nJ.png)
        
    :::info
    * 一般來講有多少 Product 就對應有多少 Factory
    :::
        
        
### Abstract Factory 設計 - 優缺點

* Abstract Factory 設計**優點**

    * 具體類別利用 **抽像解偶，基於介面與實作的分離**，由繼承者實作方法
    
    * 可以使用反射方式可以減少實作工廠類
        
        :::warning
        * 但使用反射技巧後，相對的使用者必須知道細節，**這不符合 `最少知識原則`**，在使用時要自己做些取捨
        :::

* Abstract Factory 設計**缺點**

    * **Product 新增的功能，代表所有類都要新增這項功能，不符合開閉原則 (這也是里式原則的缺點)**；可以使用 `介面隔離` 原則，讓介面立度最小化 
        
        :::success
        * 上面所說的是直向拓展（繼承）的 `Product` 會影響到各個子類別；但橫向拓展的 `Factory` 就不會受到這個影響
        :::


## Abstract Factory 實做

以下我們實現一組通訊協議

* 這組通訊協議作為產品（`Product`）需要提供創建通訊物件（`create`）、取得起始碼（`getStartCode`）這兩個功能

* 而實作的工廠（`Factory`）需要提供使用者 `SPI`、`UART`、`MIX` 通訊協議

### Abstract Factory 標準

1. **`IProduct A`、`B` 類** ：首先決定抽象產品，這邊我們要定義一組通訊協定，這個協定有 Create、Start code ... 等等規範，但是實際要由通訊方案決定
    
    :::info
    * 說明產品的共同性，而這個共通性我們就可以使用抽象來表達
    :::
    ```java=
    // 抽象產品

    public interface IComm {
    
        // 產品 1
        interface ICreate {
            void create();
        }

        // 產品 2
        interface IStart {
            String getStartCode();
        }
    }
    ```
    
2. **`ConcreateProdcutA1`、`A2`、`B1`、`B2` 類**：定義產品的實做
    ```java=
    // 定義產品的實做

    public class CreateSPI implements IComm.ICreate {

        @Override
        public void create() {
            System.out.println("Start create Spi comm");
        }

    }

    // ---------------------------------------------------------------
    public class CreateUART implements IComm.ICreate {

        @Override
        public void create() {
            System.out.println("Start create UART comm");
        }

    }

    // ---------------------------------------------------------------
    public class StartSPI implements IComm.IStart {

        @Override
        public String getStartCode() {
            return "SSS_PPP_III";
        }

    }

    // ---------------------------------------------------------------
    public class StartUART implements IComm.IStart {

        @Override
        public String getStartCode() {
            return "UUU_AAA_RRR_TTT";
        }

    }
    ```


3. **`IFactory` 類**：定義抽象工廠，該工廠又依賴於 IProduct (抽象依賴抽象)
    :::info
    * 說明工廠的要產的目標產品，由於目標產品有分類，這邊我們也用抽象來描述工廠，細節交給實作類
    :::

    ```java=
    // 定義抽象工廠

    public interface IFactory {

        IComm.ICreate create();

        IComm.IStart getStartCode();

    }
    ```
    
4. **`ConcreateFactoryA`、`B`** ：定義工廠實做，這裡我們特別定義了一個混核通訊，代表抽象工廠關注了具體產品，來產生不同維度的產品
    ```java=
    // 定義工廠實做

    public class SPI_Factory implements IFactory {

        @Override
        public IComm.ICreate create() {
            return new CreateSPI();
        }

        @Override
        public IComm.IStart getStartCode() {
            return new StartSPI();
        }
    }

    // ---------------------------------------------------------------
    public class UART_Factory implements IFactory {

        @Override
        public IComm.ICreate create() {
            return new CreateUART();
        }

        @Override
        public IComm.IStart getStartCode() {
            return new StartUART();
        }
    }

    // ---------------------------------------------------------------
    // 混合通訊

    public class MIX_Factory implements IFactory {

        @Override
        public IComm.ICreate create() {
            return new CreateSPI();
        }

        @Override
        public IComm.IStart getStartCode() {
            return new StartUART();
        }
    }
    ```

* **User 使用抽象工廠**：

    SPI & UART 實作方式不同 (對於同一種操作有不同反應)，這時就可以使用抽象類去拓展，製作出相同動作，但不同細節的方式

    ```java=
    // User 直接使用工廠

    public class FactoryTest {

        public static void main(String[] args) {
            FactoryTest factoryTest = new FactoryTest();

            // 由工廠來產生不同目標
            factoryTest.testFactory(new SPI_Factory());
            factoryTest.testFactory(new UART_Factory());
            factoryTest.testFactory(new MIX_Factory());
        }

        public void testFactory(IFactory factory) {

            System.out.println("Factory implement: " + factory.getClass().getSimpleName());

            IComm.ICreate iCreate = factory.create();
            iCreate.create();

            IComm.IStart startCode = factory.getStartCode();
            System.out.println("Comm start code: " + startCode.getStartCode());

            System.out.println("// -------------------------------\n");
        }
    }
    ```

    **--實作--**
    > ![](https://i.imgur.com/5ryEZOI.png)


### Abstract Factory 變型- 反射

* 我們在 Factory 時也有使用到反射工廠，相同的在抽象工廠中也可以使用，概念是差不多的，但這裡會創建更多工廠，看看反射工廠可以有那些好處

    1. **`IProudct`** A、B：這部分沒有變動，仍是要生產的目標產品 (抽象)

        :::info
        * 說明產品的共同性，而這個共通性我們就可以使用抽象來表達
        :::

        ```java=
        public interface IComm {

            // 產品 1
            interface ICreate {
                void create();
            }

            // 產品 2
            interface IStart {
                String getStartCode();
            }

        }
        ```

    2. **`ConcreateProdcut`** A1、A2、B1、B2：這部分沒有變動，定義產品的實做
        ```java=
        // 定義產品的實做

        public class CreateSPI implements IComm.ICreate {

            @Override
            public void create() {
                System.out.println("Create SPI comm.");
            }

        }

        // ---------------------------------------------------------------
        public class CreateUART implements IComm.ICreate {

            @Override
            public void create() {
                System.out.println("Create UART comm.");
            }

        }

        // ---------------------------------------------------------------
        public class StartSPI implements IComm.IStart {

            @Override
            public String getStartCode() {
                return "SSS_PPP_III";
            }

        }

        // ---------------------------------------------------------------
        public class StartUART implements IComm.IStart {

            @Override
            public String getStartCode() {
                return "UUU_AAA_RRR_TTT";
            }

        }
        ```

    3. **`IFactory`**：這裡要做修改，**接收一個目標 class，透過 class 來創建目標類**
        ```java=
        public interface IFactory {

            // 接收目標 class
            IComm.ICreate create(Class<? extends IComm.ICreate> clz);

            // 接收目標 class
            IComm.IStart getStartCode(Class<? extends IComm.IStart> clz);

        }
        ```

    4. **`ConcreateFactory`**：這裡要做修改，**收到目標 class 後，用反射創建目標類，如果錯誤也會拋出**

        ```java=
        public class UniversalFactory implements IFactory {

            @Override
            public IComm.ICreate create(Class<? extends IComm.ICreate> clz) {
                IComm.ICreate result;

                try {
                    Constructor<? extends IComm.ICreate> constructor = clz.getConstructor();
                    result = constructor.newInstance();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }

                return result;
            }

            @Override
            public IComm.IStart getStartCode(Class<? extends IComm.IStart> clz) {
                IComm.IStart result;

                try {
                    Constructor<? extends IComm.IStart> constructor = clz.getConstructor();
                    result = constructor.newInstance();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }

                return result;
            }

        }
        ```

    * User 使用：透過只用者指定細節
        ```java=
        public class FactoryTest {

            public static void main(String[] args) {
                FactoryTest factoryTest = new FactoryTest();

                IFactory factory = new UniversalFactory();

                System.out.println("SPI ------------------------------------");
                factoryTest.testFactory(factory, CreateSPI.class, StartSPI.class);

                System.out.println("UART -------------------------------\n");
                factoryTest.testFactory(factory, CreateUART.class, StartUART.class);

                System.out.println("MIX -------------------------------\n");
                factoryTest.testFactory(factory, CreateSPI.class, StartUART.class);
            }

            public void testFactory(IFactory factory,
                                    Class<? extends IComm.ICreate> create,
                                    Class<? extends IComm.IStart> start) {

                System.out.println("Factory implement: " + factory.getClass().getSimpleName());

                IComm.ICreate iCreate = factory.create(create);
                iCreate.create();

                IComm.IStart startCode = factory.getStartCode(start);
                System.out.println("Comm start code: " + startCode.getStartCode());

            }
        }
        ```
        :::warning
        * 在這裡 **不符合最少知識原則**，因為使用者必須知道自己要使用的細節
        :::
        > ![](https://i.imgur.com/ndVPBke.png)


        **--實作--**
        > ![](https://i.imgur.com/YF83u8l.png)


    :::warning
    * **變型反射有幾個缺點**

        ^1.^ 客戶端需知道具體細節由誰達成(因為必須指定 class 類)，並且雖然透過反射可以減少類的生成，但是相對的 ^2.^反應時間較慢
    :::


## Android Source MediaPlayer


### [MediaPlayerFactory](https://cs.android.com/android/platform/superproject/+/master:frameworks/av/media/libmediaplayerservice/MediaPlayerFactory.h)

* MediaPlayerFactory 會為不同  定義不同的類，而每種不同的 MediaPlayer 最終都會調用到 MediaPlayerFactory#registerFactory 方法

    | 類 | 角色 |
    | -------- | -------- |
    | IFactory | 抽象工廠 |
    | NuPlayerFactory、TestPlayerFactory | 抽象實體工廠 |
    | [**MediaPlayerBase**](https://cs.android.com/android/platform/superproject/+/master:frameworks/av/media/libmediaplayerservice/include/MediaPlayerInterface) | 抽象產品 (IProduct) |
    | NuPlayerDriver、TestPlayerStub | 實際產品 |
    | MediaPlayerFactory | 使用者接觸的工廠入口，組裝抽象工廠、產品 |

    > ![](https://i.imgur.com/gn8zshp.png)

    * 抽象工廠 IFactory： `IFactory` 與抽象產品 `MediaPlayerBase` 有關
        ```cpp=
        class IFactory {
          public:
            virtual ~IFactory() { }

            ... 省略部分

            virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
                                       const sp<DataSource> &/*source*/,
                                       float /*curScore*/) { return 0.0; }

            virtual sp<MediaPlayerBase> createPlayer(pid_t pid) = 0;
        };
        ```
        
    * 抽象產品 IProduct：`MediaPlayerBase` 定義應該要產的生的產品，細節交給子類

        ```java=
        class MediaPlayerBase : public RefBase
        {
            ...

            virtual status_t    initCheck() = 0;
            virtual bool        hardwareOutput() = 0;
            
            
            virtual status_t    setDataSource(
                    const sp<IMediaHTTPService> &httpService,
                    const char *url,
                    const KeyedVector<String8, String8> *headers = NULL) = 0;

            virtual status_t    setDataSource(int fd, int64_t offset, int64_t length) = 0;
            
            ... 省略部分
        }
        ```

    * MediaPlayerFactory 是工廠的使用入口，定義如何組裝抽象工廠（IFactory）、抽象產品（MediaPlayerBase）

        ```java=
        // MediaPlayerFactory.h

        class MediaPlayerFactory {

          public:

            // 宣告
            static sp<MediaPlayerBase> createPlayer(player_type playerType,
                                                    const sp<MediaPlayerBase::Listener> &listener,
                                                    pid_t pid);
        }

        // ----------------------------------------------------------
        // MediaPlayerFactory.cpp

        MediaPlayerFactory::tFactoryMap MediaPlayerFactory::sFactoryMap;

        sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
                player_type playerType,        // Player 類型
                const sp<MediaPlayerBase::Listener> &listener,    // 監聽器
                pid_t pid) {

            sp<MediaPlayerBase> p;
            IFactory* factory;
            status_t init_result;

            Mutex::Autolock lock_(&sLock);

            if (sFactoryMap.indexOfKey(playerType) < 0) {
                ... 尚未註冊
                return p;
            }

            // 決定 factory 的實作類
            factory = sFactoryMap.valueFor(playerType);

            CHECK(NULL != factory);

            // 呼叫抽象工廠 createPlayer 方法
            p = factory->createPlayer(pid);

            if (p == NULL) {
                ... 省略失敗 log
                return p;
            }

            // 呼叫抽象產品 initCheck 方法
            init_result = p->initCheck();

            if (init_result == NO_ERROR) {
                p->setNotifyCallback(listener);
            } else {
                ... 省略失敗 log
                p.clear();
            }

            return p;
        }
        ```

### MediaPlayer 註冊工廠

* 我們知道使用者會透過 MediaPlayerFactory 來使用 IFactory (抽象工廠)，而 IFactory 的實作有 `NuPlayerFactory`、`TestPlayerFactory` 兩個類，這兩個類會在 MediaPlayerFactory 建構函數時被註冊

    1. **`MediaPlayerFactory` 建構函數**：註冊 MediaPlayerBase 時做

        ```java=
        // MediaPlayerService.java

        MediaPlayerService::MediaPlayerService()
        {
            ALOGV("MediaPlayerService created");
            mNextConnId = 1;

            // @ 追蹤 registerBuiltinFactories 方法
            MediaPlayerFactory::registerBuiltinFactories();
        }
        ```
    2. **`registerBuiltinFactories` 方法**：創建 `NuPlayerFactory`、`TestPlayerFactory` 實體工廠，並透過 `registerFactory_l` 方法註冊工廠
        ```java=
        // MediaPlayerService.java

        Mutex MediaPlayerFactory::sLock;
        bool MediaPlayerFactory::sInitComplete = false;

        void MediaPlayerFactory::registerBuiltinFactories() {
            Mutex::Autolock lock_(&sLock);

            if (sInitComplete)    // 避免反覆註冊
                return;

            IFactory* factory = new NuPlayerFactory();
            if (registerFactory_l(factory, NU_PLAYER) != OK)
                delete factory;    // 創建失敗則解構
            
            factory = new TestPlayerFactory();
            if (registerFactory_l(factory, TEST_PLAYER) != OK)
                delete factory;    // 創建失敗則解構

            sInitComplete = true;
        }
        ```
    
    3. **`registerFactory_l` 方法**：將 IFactory 物件暫存在 MediaPlayerFactory 中，方便在下是需要時快速返回，避免建構重複物件
        ```java=
        MediaPlayerFactory::tFactoryMap MediaPlayerFactory::sFactoryMap;

        status_t MediaPlayerFactory::registerFactory_l(IFactory* factory,
                                                       player_type type) {
            if (NULL == factory) {
                ... 判空
                return BAD_VALUE;
            }

            // 查看是否已經註冊
            if (sFactoryMap.indexOfKey(type) >= 0) {
                ... 已註冊
                return ALREADY_EXISTS;
            }

            // 添加緩存
            if (sFactoryMap.add(type, factory) < 0) {
                ...添加進緩存失敗
                return UNKNOWN_ERROR;
            }

            return OK;
        }
        ```

## 更多的物件導向設計

物件導向的設計基礎如下，如果是初學者或是不熟悉的各位，建議可以從這些基礎開始認識，打好基底才能走個更穩（在學習的時候也需要不斷回頭看）！

:::info
* [**設計建模 2 大概念- UML 分類、使用**](https://devtechascendancy.com/introduction-to-uml-and-diagrams/)

* [**物件導向設計原則 – 6 大原則(一)**](https://devtechascendancy.com/object-oriented-design-principles_1/)

* [**物件導向設計原則 – 6 大原則(二)**](https://devtechascendancy.com/object-oriented-design-principles_2/)
:::


### 創建模式 - Creation Patterns

* [**創建模式 PK**](https://devtechascendancy.com/pk-design-patterns-factory-builder-best/)

* **創建模式 - `Creation Patterns`**：
    
    創建模式用於「**物件的創建**」，它關注於如何更靈活、更有效地創建對象。這些模式可以隱藏創建對象的細節，並提供創建對象的機制，例如單例模式、工廠模式… 等等，詳細解說請點擊以下連結

    :::success
    
    * [**Singleton 單例模式 | 解說實現 | Android Framework Context Service**](https://devtechascendancy.com/object-oriented_design_singleton/)

    * [**Abstract Factory 設計模式 | 實現解說 | Android MediaPlayer**](https://devtechascendancy.com/object-oriented_design_abstract-factory/)

    * [**Factory 工廠方法模式 | 解說實現 | Java 集合設計**](https://devtechascendancy.com/object-oriented_design_factory_framework/)

    * [**Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗**](https://devtechascendancy.com/object-oriented_design_builder_dialog/)
    
    * [**Clone 原型模式 | 解說實現 | Android Framework Intent**](https://devtechascendancy.com/object-oriented_design_clone_framework/)

    * [**Object Pool 設計模式 | 實現與解說 | 利用 JVM**](https://devtechascendancy.com/object-oriented_design_object-pool/)

    * [**Flyweight 享元模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_flyweight/)
    :::


### 行為模式 - Behavioral Patterns

* [**行為模式 PK**](https://devtechascendancy.com/pk-design-patterns-cmd-strat-state-obs-chain/)

* **行為模式 - `Behavioral Patterns`**：
    
    行為模式關注物件之間的「**通信**」和「**職責分配**」。它們描述了一系列對象如何協作，以完成特定任務。這些模式專注於改進物件之間的通信，從而提高系統的靈活性。例如，策略模式、觀察者模式… 等等，詳細解說請點擊以下連結

    :::warning
    * [**Stragety 策略模式 | 解說實現 | Android Framework 動畫**](https://devtechascendancy.com/object-oriented_design_stragety_framework/)

    * [**Interpreter 解譯器模式 | 解說實現 | Android Framework PackageManagerService**](https://devtechascendancy.com/object-oriented_design_interpreter_framework/)

    * [**Chain 責任鏈模式 | 解說實現 | Android Framework View 事件傳遞**](https://devtechascendancy.com/object-oriented_design_chain_framework/)

    * [**State 狀態模式 | 實現解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_state/)

    * [**Specification 規格模式 | 解說實現 | Query 語句實做**](https://devtechascendancy.com/object-oriented_design_specification-query/)

    * [**Command 命令、Servant 雇工模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_command_servant/)

    * [**Memo 備忘錄模式 | 實現與解說 | Android Framwrok Activity 保存**](https://devtechascendancy.com/object-oriented_design_memo_framework/)

    * [**Visitor 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_visitor_dispatch/)

    * [**Template 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_template/)

    * [**Mediator 模式設計 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_mediator/)

    * [**Composite 組合模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_composite/)
    :::


### 結構模式 - Structural Patterns

* [**結構模式 PK**](https://devtechascendancy.com/pk-design-patterns-proxy-decorate-adapter/)

* **結構模式 - `Structural Patterns`**：
    
    結構模式專注於「物件之間的組成」，以形成更大的結構。這些模式可以幫助你確保當系統進行擴展或修改時，不會破壞其整體結構。例如，外觀模式、代理模式… 等等，詳細解說請點擊以下連結

    :::danger
    * [**Bridge 橋接模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_bridge/)

    * [**Decorate 裝飾模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_decorate/)

    * [**Proxy 代理模式 | 解說實現 | 分析動態代理**](https://devtechascendancy.com/object-oriented_design_proxy_dynamic-proxy/)

    * [**Iterator 迭代設計 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_iterator/)

    * [**Facade 外觀、門面模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_facade/)

    * [**Adapter 設計模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_adapter/)
    :::

## Appendix & FAQ
:::info
:::

###### tags: `Java 設計模式` `基礎進階`