設計模式學習-抽象工廠模式

模式概述
提供一個創建一系列相關或相互依賴對象的接口,而無需制定它們具體的類,抽象工廠(Abstract Factory)模式又稱爲Kit模式,屬於對象創建型模式。

抽象工廠模式與工廠方法模式最大的區別在於:工廠方法模式針對的是一個產品等級結構,而抽象工廠模式則針對的是多個產品等級結構。所以在抽象工廠模式中經常會用到產品族這一概念,它指的是位於不同的產品等級結構中,並且功能互相關聯的產品系列。


UML類圖

 

其中類與對象的關係爲:

  • AbstractFactory:抽象工廠
    聲明生成抽象產品的方法

  • ConcreteFactory:具體工廠
    執行生成抽象產品的方法,生成一個具體的產品

  • AbstractProduct:抽象產品
    爲一種產品聲明接口

  • Product:具體產品
    定義具體工廠生產的具體產品的對象,實現產品接口

  • Client:客戶
    應用程序,使用抽象產品和抽象工廠生成對象。

爲了瞭解對象的創建和產品的生成過程,我們看一下抽象工廠典型應用的順序圖:

優勢劣勢:

抽象工廠模式主要優點是隔離了具體類的生成,使得客戶不需要知道什麼被創建了。由於這種隔離,使得更換一個具體的工廠變得相對容易,該模式符合GRASP純虛構的模式,實現了高內聚。主要缺點是,在添加新的產品對象時,難以擴展抽象工廠,因爲抽象工廠接口規定了所有可能被創建的產品集合,如果要支持新種類的產品,需要對工廠接口進行擴展,這就涉及到修改既有代碼,違反了開閉原則。
 

應用場景:

這個模式想必大家都十分熟悉,在開發中也會經常用到,比如說多數據庫支持,另外在下面的情景,可以考慮使用抽象工廠模式:

  1. 系統需要屏蔽有關對象如何創建、如何組織和如何表示
  2. 系統需要由關聯的多個對象來構成
  3. 有關聯的多個對象需要一起應用並且它們的約束是強迫的,不可分離的
  4. 提供一組對象而不顯示它們的實現過程,只顯示它們的接口 


應用示例:

下面實現一個有信息保存和日誌保存的小模塊,要求該模塊可以方便的將保存的位置在數據庫和XML之間進行切換。

由於保存的位置不定,所以信息保存和日誌保存會針對不同的位置,有不同的處理形式,而針對一種保存方式而言,信息和日誌的保存又形成一族,針對這些特點,我們採用抽象工廠模式來實現該模塊。

首先,我們先將信息保存和日誌保存抽象出來:

    public abstract class MessageDal
    {
        public abstract void SaveMessage();
    }

    public abstract class LogDal
    {
        public abstract void SaveLog();
    }

然後,我們根據不同的保存形式,分別實現抽象類:

    public class SqlMessageDal : MessageDal 
    {
        public override void SaveMessage()
        {
            Console.WriteLine("信息保存到數據庫中");
        }
    }

    public class XmlMessageDal : MessageDal
    {
        public override void SaveMessage()
        {
            Console.WriteLine("信息保存到XML中");
        }
    }

    public class SqlLogDal : LogDal
    {
        public override void SaveLog()
        {
            Console.WriteLine("日誌保存到數據庫中");
        }
    }

    public class XmlLogDal : LogDal
    {
        public override void SaveLog()
        {
            Console.WriteLine("日誌保存到XML中");
        }
    }
下面就該實現我們的抽象工廠及實際工廠了:
    public abstract class AbstractFactory 
    {
        public abstract MessageDal CreateMessageDal();
        public abstract LogDal CreateLogDal();
    }

    public class SqlFactory : AbstractFactory {
        public override MessageDal CreateMessageDal()
        {
            return new SqlMessageDal();
        }
        public override LogDal CreateLogDal()
        {
            return new SqlLogDal();
        }
    }

    public class XmlFactory : AbstractFactory {
        public override MessageDal CreateMessageDal()
        {
            return new XmlMessageDal();
        }

        public override LogDal CreateLogDal()
        {
            return new XmlLogDal();
        }
    }
客戶端調用如下:
    class Client
    {
        static void Main() {
            AbstractFactory factory = new SqlFactory();
            MessageDal msg = factory.CreateMessageDal();
            LogDal log = factory.CreateLogDal();
            msg.SaveMessage();
            log.SaveLog();
            factory = new XmlFactory();
            msg = factory.CreateMessageDal();
            log = factory.CreateLogDal();
            msg.SaveMessage();
            log.SaveLog();
            Console.ReadLine();
        }
    }

至此我們的小模塊實現完成了,保存位置修改,只需生成不同的工廠實例就可以了。

實際的生產環境,我們肯定一般只需要保存到一個位置,上面的客戶端代碼爲了演示,將2個位置都保存了,代碼中,爲了切換到XML保存,我們還是修改了客戶端代碼,作爲一個有追求的碼農,這是我們不想看到的。所以,我們採用反射修改一下我們的客戶端調用代碼:

        static void Main() {
            AbstractFactory factory = (AbstractFactory)Assembly.Load("Gof").CreateInstance("Gof.AbstractFactory.SqlFactory");
            MessageDal msg = factory.CreateMessageDal();
            LogDal log = factory.CreateLogDal();
            msg.SaveMessage();
            log.SaveLog();
            Console.ReadLine();
        }

由於創建實際工廠採用了反射形式,所以現在我們可以通過配置文件決定我們的保存位置了,客戶端代碼也就無需修改了。這裏不在贅述...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章