抽象工廠模式 Abstract Factory

抽象工廠模式是什麼?

抽象工廠模式是用來創建一群具有共同關聯特性的物件群的設計模式。
舉例來說,假設兩間房子,一間房子是歐式風格,一間房子是日式風格,兩間房子風格都是非常統一的,你不會希望歐式風格的房子中出現了不是歐式風格的家具或設計,你也不會希望日式風格的房子裡出現了其他風格的物品。同樣的,你也不會希望房子缺了門、少了家具、少了房間之類的情形發生。
而抽象工廠模式就是為了在建立整棟房子時,先約定好整個房子該有的物件,接著在實現時依據風格或主題去做相對應的實現。

用一句話來說,就是將製作一個複雜產品所需要的各種相關原料整合在一起。

抽象工廠模式解決了什麼問題

  1. 避免Client在自己的程式碼中為了需要特定的物件而去建立大量相關物件來組合,這會讓Client的程式碼變得複雜之外,也會難以維護和調整。
  2. 由抽象工廠生產出來的物件彼此之間,會因為統一主題的關係,而有更好的協作性與相容性。
  3. 由於生產出來的物件遵循抽象工廠所定義的模板,因此在做出差異化、功能擴增、版本更新以及維護上都相對的容易。

抽象工廠模式的概念

抽象工廠模式的目的在於整合一個複雜的物件其相關的子物件與資訊,實作時則根據實作的目標來進行內部的實現。

假設抽象工廠要生產的是手機,手機的螢幕、CPU、RAM、記憶體等不會由抽象工廠直接生產,而是專門生產這些零件的工廠所生產,抽象工廠根據不同的手機品牌規格,使用不同廠牌型號的零件組裝,最後變成各廠牌的手機產品給客戶。

抽象工廠模式的實作

這個實作會利用工廠生產出兩套風格的房子,預設風格與日式風格。 每間房子都會有一張椅子和一張桌子,桌椅的風格都會和房子的風格一致。

首先會先建立椅子工廠介面,並且從椅子的介面實作出兩種風格的椅子工廠,接著桌子工廠也是一樣的步驟。 再來建立房子的介面並實作,房子可以從Info這個變數取出房子的風格以及桌椅的風格資訊。

再來建立房子工廠介面,房子工廠介面就是抽象工廠模式,介面中整合了椅子工廠和桌子工廠的介面,並且會回傳符合房子介面條件的房子。 接著將房子工廠同樣實作成兩個風格的房子工廠,抽象工廠的實作就完成了。

在執行的Main函式上,我們先呼叫房子類別,資訊上會顯示是一間空房子,沒有椅子和桌子。 接著我們建立預設風格的房子工廠,由工廠建立一個預設風格的房子,可以看到預設風格的房子內有預設風格的椅子和桌子。 同樣的如果是建立日式風格的房子工廠,就會獲得日式風格的房子和家具。

namespace App
{
    /*   ChairFactory   */
    interface IChairFactory
    {
        string Name { get; set; }
        string CreateChair();
    }
    class DefaultStyleChairFactory : IChairFactory
    {
        public string Name { get; set; }
        public DefaultStyleChairFactory(string name = "Default Style Chair")
        {
            Name = name;
        }
        public string CreateChair()
        {
            return Name;
        }
    }
    class JPStyleChairFactory : IChairFactory
    {
        public string Name { get; set; }
        public JPStyleChairFactory(string name = "JP Style Chair")
        {
            Name = name;
        }
        public string CreateChair()
        {
            return Name;
        }
    }

    /*   DeskFactory   */
    interface IDeskFactory
    {
        string Name { get; set; }
        string CreateDesk();
    }
    class DefaultStyleDeskFactory : IDeskFactory
    {
        public DefaultStyleDeskFactory(string name = "Default Style Desk")
        {
            Name = name;
        }
        public string Name { get; set; }
        public string CreateDesk()
        {
            return Name;
        }
    }
    class JPStyleDeskFactory : IDeskFactory
    {
        public JPStyleDeskFactory(string name = "JP Style Desk")
        {
            Name = name;
        }
        public string Name { get; set; }
        public string CreateDesk()
        {
            return Name;
        }
    }

    /*   House   */
    interface IHouse
    {
        String Info { get; set; }
        String Name { get; set; }
        String Chair { get; set; }
        String Desk { get; set; }
    }
    class House : IHouse
    {
        public String Info { get; set; }
        public String Name { get; set; }
        public String Chair { get; set; }
        public String Desk { get; set; }
        public House(String name = "Empty House", String chair = "No Chair", String desk = "No Desk")
        {
            Name = name;
            Chair = chair;
            Desk = desk;
            Info = $"This is a {Name}, there are {Chair} and {Desk}.";
        }
    }

    /*   HouseFactory   */
    interface IHouseFactory
    {
        String Name { get; set; }
        IChairFactory ChairFactory { get; set; }
        IDeskFactory DeskFactory { get; set; }
        IHouse CreateHouse();
    }
    class DefaultHouseFactory : IHouseFactory
    {
        public String Name { get; set; }
        public IChairFactory ChairFactory { get; set; }
        public IDeskFactory DeskFactory { get; set; }
        public DefaultHouseFactory()
        {
            Name = "Default House";
            ChairFactory = new DefaultStyleChairFactory();
            DeskFactory = new DefaultStyleDeskFactory();
        }

        public IHouse CreateHouse()
        {
            String Chair = ChairFactory.CreateChair();
            String Desk = DeskFactory.CreateDesk();
            return new House(Name, Chair, Desk);
        }
    }

    class JPStyleHouseFactory : IHouseFactory
    {
        public String Name { get; set; }
        public IChairFactory ChairFactory { get; set; }
        public IDeskFactory DeskFactory { get; set; }
        public JPStyleHouseFactory()
        {
            Name = "JP Style House";
            ChairFactory = new JPStyleChairFactory();
            DeskFactory = new JPStyleDeskFactory();
        }

        public IHouse CreateHouse()
        {
            String Chair = ChairFactory.CreateChair();
            String Desk = DeskFactory.CreateDesk();
            return new House(Name, Chair, Desk);
        }
    }
}
namespace App
{
    class Program
    {
        public static void Main()
        {
            IHouse h = new House();
            Console.WriteLine(h.Info);

            IHouseFactory hf = new DefaultHouseFactory();
            IHouse h2 = hf.CreateHouse();
            Console.WriteLine(h2.Info);

            IHouseFactory hf2 = new JPStyleHouseFactory();
            IHouse h3 = hf2.CreateHouse();
            Console.WriteLine(h3.Info);
        }
    }
}

抽象工廠模式實際的應用案例

  1. 多資料庫選項的情境:資料庫有很多類型,像是SQL、Redis、NoSQL的類型,Client端如果需要用到不同的資料庫,直接在Client的程式碼中實作會讓程式碼變的複雜,使用抽象工廠去建立各類型資料庫的統一操作介面,就可以讓Client只需要專注在與介面的整合,不需要去考慮介面背後的資料庫是哪種資料庫,也不需要去實作個別資料庫的存取方法。

抽象工廠與工廠方法的差別

工廠方法的目標是實作要生產單一對象,而抽象工廠則是定義了透過工廠方法生產出來的產品該如何組合與協作。
以具體例子來說,抽象工廠像是蓋不同風格房子,房子裡會有同一主題風格的家具、裝潢,而工廠方法則是在生產特定的家具,像是椅子工廠、門工廠、電燈工廠等等。
在蓋房子時,不論選擇哪個風格的工廠,都會得到抽象工廠所定義的基本家具,但是風格則是由該風格所實作的家具工廠所建立出來的,因此才會得到不同風格的家具。
抽象工廠從概念上是工廠方法的延伸,但抽象工廠的組織物件同時也可以是其他的抽象工廠所生產的原件,它將重心放在整合上,因此具有高度的擴充性。

抽象工廠的缺點

對於小型應用、需要快速開發的專案來說,可能會過於複雜。

Last Updated:
Contributors: eisen