從壹開始 [ Design Pattern ] 之三 ║ 工廠模式 與 小故事


 

編者按:

定義一個用於創建對象的接口,讓子類決定實例化哪一個類。工廠方法使得一個類的實例化延遲到子類。

工廠模式,是迄今為止,使用最多,最廣泛的設計模式之一,它的身影幾乎出現在每一個框架和個人代碼之中。

它是基石,只有充分了解並掌握了工廠模式,才能繼續的向更深層的設計模式進發和努力。

今天的可能有點兒繞,如果有不懂的,可以在文章下邊留言,如果有三個以上評論要我講課的話,我就在直播課堂,給大家講解一下。

同時為了更好的講解,我會稍微穿插一個小故事,讓大家能夠很好的掌握工廠模式的三大步。

 

 一、什么是工廠模式?

 在上一篇文章中,我們說到了《單例模式》,了解到了它的場景,也學會了它的目的,從模式類型上,我們可以知道,他是一個創建型的設計模式,說白了就是創建一個對象的實例,只不過是單例的 —— 單一實例的。

那今天我們繼續說下一個創建型的設計模式 —— 工廠模式,工廠模式和單例模式,正好相反,他不是創建單一的,而是創建無窮的,隨着項目而不斷變化的實例場景。

工廠模式】,英文名稱:Factory Pattern,是開發中最常用的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。在工廠模式中我們在創建對象時不會對客戶端暴露創建邏輯,並且是通過使用一個共同的接口來指向新創建的對象

從日常使用分類上,工廠模式有三種:

1、簡單工廠模式

2、工廠方法模式

3、抽象工廠模式

不過整體上,思路是類似的,可以統稱為工廠模式,但是一般有人不認為簡單工廠屬於工廠模式,而且同時又把抽象工廠,給單獨提取出來,作為一個新的設計模式,這個無可厚非,今天我們就通過一個生活中的小栗子,然后再配合具體代碼,一步一步學習工廠模式,從簡單工廠,到工廠方法,最后到抽象工廠。

 

 

 二、簡單工廠模式

 

生活小場景第一集

老張呢,平時都是一個人住,為了健康生活,經常會自己買菜,洗衣,做飯,周末時不時的就像來點兒花樣(其實並沒有😂),比如上周末想吃【椒鹽紫蘇蝦】,是不是聽着名字就想吃,嗯,就是不好做,辛辛苦苦搞了半天,最后還是不太滿意。然后呢,這周末又想吃【紅燒話梅排骨】挑戰一下?結果到了周末,還是停留在想想的階段,想吃但是不想做!

這個時候,就特別想要一個我的廚師,我吃啥,就給我做啥,就是這么美好,只要我傳遞一個菜名參數,就會給我實例化一個菜,是真正的菜的對象示例,不是一個臆想,那作為代碼的上帝,我們來創建一個吧,一個簡單的個人廚師——簡單工廠。

 

1、先從最簡單的開始

上邊的例子大家肯定都能看懂,我就不多說了,代碼呢,本來想用這個小故事里的,只不過和我們平時開發不是很貼近,我就用平時都在用的 Repository 倉儲模式來說明下,而且我也打算下一步寫一個能兼容所有 ORM 的 Repository Lib 組件庫。

首先呢,我們平時開發的時候,如果想創建一個倉儲方法,來調取數據庫,很簡單,直接上代碼,每個人都能看懂:

 /// <summary>
 /// 定義倉儲類
 /// </summary>
 public class Repository
 {
     /// <summary>
     /// 獲取數據方法
     /// </summary>
     public void GetData()
     {
         // 可以進行各種操作,無論是EFCore的 DbSet ,
         // 還是Sqlsugar的 sugarClient
         // ...
         Console.WriteLine("獲取全部數據!");
     }
 }

 // 調用
 [HttpGet]
 public object Get()
 {
     // 實例化倉儲,然后調用
     Repository repository = new Repository();
     repository.GetData();
     return "";
 }

 

最后結果出來了:

 

是不是很簡單?!沒有任何含量,至於其他的什么上下文,咱們不管,只說調用情況,中間不論業務邏輯多復雜,咱們平時就是這么寫的,也很符合我們平時開發的邏輯。打完收工,吃蛋糕!

可能你會說,工廠呢?設計模式呢?沒有說呀,那我要提一個需求了,我們的項目需要用到多種 ORM 共存的倉儲,嗯,你怎么辦?你這個時候可能會說,簡單!看我的:

 /// <summary>
 /// 定義一個 Sqlsugar 倉儲
 /// </summary>
 public class RepositorySqlsugar
 {
     public void GetData()
     {
         Console.WriteLine("獲取 Sqlsugar 全部數據!");
     }
 }

 /// <summary>
 /// 定義一個 EFCore 倉儲
 /// </summary>
 public class RepositoryEFCore
 {
     public void GetData()
     {
         Console.WriteLine("獲取 EFCore 全部數據!");
     }
 }

  // 然后我們調取
  [HttpGet]
  public object Get()
  {
      // 實例化倉儲,然后調用
      RepositorySqlsugar repositorySqlsugar = new RepositorySqlsugar();
      repositorySqlsugar.GetData();
      RepositoryEFCore repositoryEFCore = new RepositoryEFCore();
      repositoryEFCore.GetData();

      return "";
  }

 

 

 

上邊的代碼也很簡單,相信也有部分小伙伴用過,既然是兩個ORM,那就定義兩個操作類,互相沒影響,還可以自定義擴展,然后分別去調用,沒啥難度,這個時候,我們可以看到了效果:

 

 

2、 設計模式中,要學會封裝

上邊的代碼,我們可以解決多 ORM 共存的問題,不管設計的多粗糙,反正最后我們用到了 EFCore 和 SqlSugar 兩個 ORM,你還可以自定義去調用,但是我們平時開發的時候知道,面向對象三大特性——封裝、繼承、多態。要學會去封裝,不能隨意的寫很多的類,畢竟內容基本是一樣的,好!那既然要封裝,我們就抽象出來一個公共的 Repository,然后繼承它就行了:

 /// <summary>
 /// 定義倉儲抽象類
 /// </summary>
 public abstract class Repository
 {
     /// <summary>
     /// 抽象方法,獲取數據
     /// </summary>
     public abstract void GetData();
 }


 /// <summary>
 /// 定義一個 EFCore 倉儲
 /// 繼承倉儲父類
 /// </summary>
 public class RepositoryEFCore : Repository
 {
     /// <summary>
     /// 實現父類抽象方法
     /// </summary>
     public override void GetData()
     {
         Console.WriteLine("獲取 EFCore 全部數據!");
     }
 }

/// <summary>
/// 定義一個 Sqlsugar 倉儲
/// </summary>
public class RepositorySqlsugar : Repository
{
    public override void GetData()
    {
        Console.WriteLine("獲取 Sqlsugar 全部數據!");
    }
}

 

這個時候相信大家都能明白了,我們用兩個類,去繼承抽象倉儲父類,使用的時候,只需要根據需要,實例不同的倉儲子類就行了,這樣就可以任意的使用:

 /// <summary>
 /// 根據不同的類型,來實例化不同的對象
 /// </summary>
 /// <param name="type"></param>
 /// <returns></returns>
 public Repository GetRepository(string type)
 {
     Repository repository = null;
     if (type.Equals("sugar"))
     {
         repository = new RepositorySqlsugar();
     }
     else if (type.Equals("efcore"))
     {
         repository = new RepositoryEFCore();
     }
     return repository;
 }

 // 然后去調用
 [HttpGet]
 public object Get()
 {
     // 實例化倉儲,然后調用
     Repository sugar = GetRepository("sugar");
     sugar.GetData();

     Repository efcore = GetRepository("efcore");
     efcore.GetData();

     return "";
 }

 

然后我們直接來看看效果:

 

 

依然可以!怎么樣!是不是有那么點兒意思了,是不是有設計感了,概括來說,我們通過一個方法,來控制我們的不同對象的實例輸出,從而實現封裝、繼承、多態!是不是很像我們的個人管家,只要傳遞過去一個參數,就可以給我們生成多個對象實例。

到了這里,我們可以長舒一口氣,工廠模式這么簡單的么?!不,還遠遠沒有到,我再來問一個問題,項目我的 api 層雖然封裝了這個公共倉儲方法,來控制輸出,但是我們也同時依賴了這些子類倉儲,你想想是不是,這個時候我們就稍微簡單的修改一下就好 —— 為了降低對象之間的耦合。

 

3、降低對象之間的耦合

 

那這個時候我們應該怎么辦呢,很簡單,建一個靜態方法就行了,這樣就提取出去了:

/// <summary>
/// 定義簡單倉儲工廠
/// </summary>
public class RepositorySimpleFactory
{
    /// <summary>
    /// 定義靜態方法,控制實例對象輸出
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public static Repository GetRepository(string type)
    {
        Repository repository = null;
        if (type.Equals("sugar"))
        {
            repository = new RepositorySqlsugar();
        }
        else if (type.Equals("efcore"))
        {
            repository = new RepositoryEFCore();
        }
        return repository;
    }
}

 [HttpGet]
 public object Get()
 {
     // 靜態方法獲取不同實例,對象之間降低耦合
     Repository sugar = RepositorySimpleFactory.GetRepository("sugar");
     sugar.GetData();

     Repository efcore = RepositorySimpleFactory.GetRepository("efcore");
     efcore.GetData();

     return "";
 }

 

結果不用看了,依然是有效的,沒錯,你已經不知不覺中學會了簡單工廠模式!這次真的很簡單了,從上邊我們看出來了,我們一步一步的往下走,相信每個人都能看懂他的意義,這個時候,我們可以驕傲的說,我們學會了簡單工廠!

但是我們的目的,是工廠方法,簡單工廠只是一個開胃菜,這個時候,我們稍微緩一緩,如果上邊的你都看懂了,可以思考一個問題,如果沒看懂,請繼續刷第二遍,

 

那問題就是:

簡單工廠,我們是通過一個變量 type 來進行區分的,來創建不同的子類對象實例的,這樣不好,因為如果我們以后要增加 dapper 的話,我們還是需要修改這個簡單工廠里的方法,如果很多的話,不僅麻煩,也不符合我們的六大設計模式原則中的其中一個原則,就是OCP原則,中文是【開放關閉原則】,對增加代碼開發,對修改代碼關閉。

六大原則:

【單一職責原則】,【里氏替換原則】,【依賴倒置原則】,【接口隔離原則】、【迪米特法則】和【開閉原則】

 

那我們應該如何做,才能滿足我們的對修改關閉,對添加開放的原則呢?

沒錯,就是今天的第一個重頭戲 —— 工廠方法模式!

 

 三、工廠方法模式

 

生活場景第二集:

老張吃中餐吃的越來越多了,有點兒膩了,那咋辦,換個口味唄,打算吃西餐了,比如【魚子醬】,比如【奶油蛋糕】,比如【法式焗蝸牛】,聽着還不錯,直接安排!

我不是有一個私人廚師嘛,交給他,反正只要我們傳遞一個菜名,就能創建實例,多好。

然后過了一段時間,廚師離職了。🙃

大家肯定也能猜得出來,第一是自己忙不過來,第二就是以后我們創建任何一個新的對象實例的時候,都需要修改內部方法,讓這個簡單工廠越來越大,越來越笨重,違背了我們的OCP原則。

后來我想通了,既然是吃貨,就直接再招一個西點師傅吧,負責西餐,中餐師傅做中餐,各司其職,然后再來一個個人助理,統一協調。

只要我想吃一道菜,私人助理就會自動給我創建指定的廚師,這就是工廠方法模式。

甚至以后我想再吃一個風格的菜,比如面點,直接讓個人助理招一個面點師就行,實現了對內修改關閉,對外新增開放的原則

 

剛剛我們文章的開頭,我們說到了,我們可以通過定義多個類,來進行不同 ORM 的划分,然后我們也封裝了一個GetRepository 的方法,實現了【哪里有變化,哪里就封裝】的設計思路,那我們可以借着這個思路,我們可以建立多個工廠來實現。

 

說白了就是,我們抽象出來一個工廠,這個工廠用來生產不同的 Repository 對象實例,從而實現目的,那我們就直接開始動手

 /// <summary>
 /// 1、抽象工廠類
 /// </summary>
 public abstract class RepositoryFactory
 {
     /// <summary>
     /// 抽象方法,用來返回倉儲對象
     /// </summary>
     /// <returns></returns>
     public abstract Repository CreateRepository();
 }

 /// <summary>
 /// 2.1、EFCore 的倉儲工廠
 /// 繼承抽象倉儲工廠
 /// </summary>
 public class RepositoryFactory_EFCore : RepositoryFactory
 {
     /// <summary>
     /// 重寫,生成EFCore 倉儲的實例
     /// </summary>
     /// <returns></returns>
     public override Repository CreateRepository()
     {
         return new RepositoryEFCore();
     }
 }

/// <summary>
/// 2.2、SqlSugar 的倉儲工廠
/// 繼承抽象倉儲工廠
/// </summary>
public class RepositoryFactory_SqlSugar : RepositoryFactory
{
    /// <summary>
    /// 重寫,生成 SqlSugar 倉儲的實例
    /// </summary>
    /// <returns></returns>
    public override Repository CreateRepository()
    {
        return new RepositorySqlsugar();
    }
}

 

 

這里我們可以看到,我們是在倉儲的基礎上,抽象了一套工廠模式,從之前我們通過 type 類型來判斷,生成倉儲實例,變成了,通過不同的倉儲工廠來生成對應倉儲對象實例。

然后我們來調用一下:

 [HttpGet]
 public object Get()
 {
     // 初始化創建Repository的兩個倉儲工廠
     RepositoryFactory efcoreRepositoryFactory = new RepositoryFactory_EFCore();
     RepositoryFactory sugarRepositoryFactory = new RepositoryFactory_SqlSugar();

     // 生產efcore倉儲的實例
     var efcoreRepository = efcoreRepositoryFactory.CreateRepository();
     efcoreRepository.GetData();

     //生產sugar倉儲的實體
     var sugarRepository = sugarRepositoryFactory.CreateRepository();
     sugarRepository.GetData();

     return "";
 }

 

 

結果依然正確:

 

 

 

好啦!這次可以真正的歇歇了,工廠方法模式,已經正式完成了,可能越往后越復雜,不過自己簡單想一想,還是能夠明白的。

你可能會說,說了這么多,這個和文章開頭,我們定義兩個倉儲類,直接輸出不一樣么,現在搞的這么復雜:

 

 

 

真的有必要么?當然!這個還是一個小的項目,如果你看看我的 Blog.Core 項目就知道了,那么小的還有十多個倉儲,中型項目幾十個上百個,然后呢,如果我們要同時使用四種 ORM:EFCore、SqlSugar、Dapper、Ado 等等等等,還有事務操作,所以工廠方法模式,還是很有必要的,除了簡單代碼量,而且更符合我們開發設計思想:封裝,繼承,多態,OCP原則等等。

上邊我們建立好了工廠方法,如果我們現在需要創建一個 Dapper 的ORM,我們什么都不需要修改,只需要添加幾個類即可:

1、定義一個 RepositoryDapper 倉儲類;

2、定義一個 RepositoryFactory_Dapper 倉儲工廠類;

 

為了驗證大家的學習是否成功,代碼我就不寫了,自己可以練習喲,有問題,歡迎留言。

大家學到了這里,我建議自己可以練習練習,可以寫一下部分代碼,同時呢,也很好的區分下【簡單工廠】和【工廠方法】之間的區別和練習,如果說你已經看懂了,或者說自己已經練習好了,那咱們就繼續往下說今天的第二個重要知識點 —— 抽象工廠;

 

再說抽象工廠之前呢,咱們先簡單總結一下:

1、我們知道,工廠模式屬於創建型開發模式的一元,他的作用就是創建我們需要的對象,如果一個一個創建的話,會很麻煩,所以我們誕生出來了一個【簡單工廠】,這個簡單工廠只是簡單的人為的把幾個對象的實例給堆起來,通過type 來區分,然后分別 new 實例化,有時候也是一個很好的方案,但是這樣有一個弊端,違背了我們開發六大原則中的——OCP開放關閉原則,所以這個時候,我們就又多出來一個新的概念【工廠方法】。

2、【工廠方法】是在【簡單工廠】的基礎上,做了相應的改良——通過多個子類的形式,來替換之前的 type 分類法,對內修改關閉,對外新增打開,這樣無論是代碼整潔上,還是擴展封裝上,都有了很好的體驗,同時也滿足了我們的OCP開發原則。

3、但是!這種方案真的就很好了么,我們再來會議一下,我們無論是簡單工廠,還是工廠方法,都是生成的單獨的一個類,好處是可以一一的慢慢擴展,但歸根結底還是在處理一個類,我們平時開發的時候,處理一個類是很多,比如常見的helper,Log 之類的。但是這不是我們開發的重點,我們平時使用最多的還是 Service 類,或者 Repository 類,里邊有很多,各種各樣的的類,比如 User 表,Role 表,Permission 表等等,每一個實體又都對應各自的一個服務類或者倉儲類。

4、那這個時候,我們使用上邊的【工廠方法】還行么?肯定是不行的!因為我們上邊是一個二維體系,EFCoreRepository 、SugarRepository、DapperRepository等等,是這樣的二維,我們現在要做的就是在這個二維的基礎上,再加上一個維度,就是要解決 User 、Role、Permission 實體等等這種一組或者一系列的類創建的問題。

5、那就是今天下邊要說到的【抽象工廠】模式。

 

注意,下邊的例子可能不太恰當,只是作為理解抽象工廠模式來使用,具體開發中,可能有出入。

 

 四、抽象工廠模式

 

生活場景第三集:

老張生活的美滋滋,天天不愁吃,嗯還押上了🤣,有一天老李也想要廚師,那咋辦,我大筆一揮,簡單!安排上。

我就通知我的個人助理,幫老李也找了三個廚師——中餐,西餐,面點,那現在就有六個了。

就這樣過了一段時間,個人助理辭職了?!

情況就是,他說太忙了,而且需求也是各種各樣,相同的一個系列,需要准備很多,還有我的其他的小伙伴也讓他來找廚師,好幾組,自己的小本本上,密密麻麻的都是記錄的各種安排。

明白了,如果是一系列的,一組對象實例創建的話,如何還用工廠方法的話,就會出問題,而且還同時破壞了OCP原則,大家想想是不是,對內修改關閉原則

那這個時候怎么辦呢,欸!我再抽象一層,把我的這個個人助理,也就是這個工廠,給增加一個大管家中介,這里是不是又有了一個中介者模式的味道😂,所以說,設計模式都有相似性。

那這個大管家中介,就是一個抽象工廠,他負責管理多個組多個系列,也就是對應多個私人助理,以及下邊的廚師。

 

上邊的問題我們都看到了,我們要解決一系列一組類創建的問題,引申出來了抽象工廠模式,那下邊我們就簡單寫一些代碼,看看是否跑的通。

1、創建一個核心層,添加多個倉儲操作

我們畢竟要操作數據庫嘛,所以肯定需要倉儲來持久化,那我們就創建一個 FactoryPattern.Core 層,用來存放我們整個項目核心的底層。首先要創建的就是幾個倉儲:

 

/// <summary>
/// 定義抽象的基類倉儲
/// </summary>
public abstract class BaseRepository
{
    /// <summary>
    /// 創建
    /// </summary>
    public abstract void Add();
    /// <summary>
    /// 刪除
    /// </summary>
    public abstract void Delete();
    /// <summary>
    /// 修改
    /// </summary>
    public abstract void Update();
    /// <summary>
    /// 查詢
    /// </summary>/ 
    public abstract void Query();
}



  /// <summary>
  /// 定義抽象用戶倉儲,繼承抽象基類倉儲
  /// 抽象的目的,是為了給UserRepositoryEFCore、UserRepositorySugar、
  /// 做父類
  /// </summary>
  public abstract class UserRepository: BaseRepository
  {
  }


 /// <summary>
 /// 同 UserRepository
 /// </summary>
 public abstract class RoleRepository: BaseRepository
 {
 }

 /// <summary>
 /// 同 UserRepository
 /// </summary>
 public abstract class PermissionRepository: BaseRepository
 {
 }

 

 

那基本的倉儲都已經定義好了,現在就需要一個工廠來生產這一系列產品了,所以我們定義一個抽象工廠類:

 /// <summary>
 /// 抽象工廠類,提供創建不同倉儲接口
 /// </summary>
 public abstract class AbstractFactory
 {
     // 抽象工廠提供創建一系列產品的接口
     public abstract UserRepository UserRepository();
     public abstract RoleRepository RoleRepository();
     public abstract PermissionRepository PermissionRepository();
 }

 

結構如下:

 

 

 

 

2、創建EFCore倉儲工廠層

說人話就是,剛剛我們不是定義了一個抽象的工廠么,用來生產我們數據庫中一系列一組的產品,也就是數據庫表,那現在我們就需要指定具體的工廠來生產他們了,首先第一個我們就用EFCore這個工廠來生產,創建一個 FactoryPattern.Repository.EFCore 類庫,並引用 Core 核心層

 

 

 

首先呢,我們就要在這一層中,對那幾個抽象的倉儲類做重寫,對應每一個EFCore 版本的倉儲類,可能你會問為什么,要每一個重寫下,還是OCP原則,而且還有一個願意,Sqlsugar 可能某些表達式查詢,在EFCore里不能用,所以必須每一個重寫出來。

這里有一個地方就是,可以在EFCore也針對基類倉儲做一個基類的,但是后來有類型不一致問題,大家可以自己看看.

 /// <summary>
 /// EFCore User 倉儲,繼承User倉儲
 /// </summary>
 public class UserRepositoryEFCore : UserRepository
 {
     public override void Add()
     {
         throw new NotImplementedException();
     }

     public override void Delete()
     {
         throw new NotImplementedException();
     }

     public override void Query()
     {
         throw new NotImplementedException();
     }

     public override void Update()
     {
         throw new NotImplementedException();
     }
 }


// 其他兩個表也是這個情況,不粘貼代碼了。

 

那現在有了子倉儲產品了,我們就開始加工生產了,創建 EFCoreRepositoryFactory.cs ,並繼承抽象工廠,同時實現抽象方法:

 /// <summary>
 /// EFCore 倉儲子工廠
 /// 用來生產各個實體倉儲
 /// </summary>
 public class EFCoreRepositoryFactory : AbstractFactory
 {
     public override PermissionRepository PermissionRepository()
     {
         return new PermissionRepositoryEFCore();
     }

     public override RoleRepository RoleRepository()
     {
         return new RoleRepositoryEFCore();
     }

     public override UserRepository UserRepository()
     {
         return new UserRepositoryEFCore();
     }
 }

 

 

結構如下:

 

 

 

 

 

 

3、同理,創建Sugar倉儲工廠層

過程和上邊的一模一樣,我就不多說了,整體結構還是這樣的:

 

 

 

 

4、控制器調用實例

我們在 api 層,引用剛剛創建的兩個倉儲層項目:

 

 

然后開始調用:

[HttpGet]
public void Get()
{
    // 實例化工廠,這里用來生產 efcore 這一系列的 產品
    AbstractFactory efcoreFactory = new EFCoreRepositoryFactory();
    efcoreFactory.UserRepository().Add();
    efcoreFactory.RoleRepository().Delete();
    efcoreFactory.PermissionRepository().Query();


    // 實例化工廠,這里用來生產 sugar 這一系列的 產品
    AbstractFactory sugarFactory = new SugarRepositoryFactory();
    sugarFactory.UserRepository().Add();
    sugarFactory.RoleRepository().Delete();
    sugarFactory.PermissionRepository().Query();
}

 

結果我就不調試,肯定是沒有問題的,畢竟僅僅是類的調用嘛,如果說你從上邊往下看,看到了這里,還沒有問題,並且能大概明白其中的意義,那你的工廠模式已經完全學會了,特別是這個抽象工廠,一直很繞,而且也使用的不是很直觀,用途不是很多。

 

5、抽象工廠和工廠方法的區別

現在我們再簡單的說明一下,我們通過【抽象工廠】模式,慢慢的明白了,其實抽象工廠是在【工廠方法】模式的基礎上,往外又多做了一套封裝,目的就是解決生產一系列產品的時候,工廠方法無法滿足的問題。

所以這個時候,如果有人問你二者的區別,核心的區別就是:如果是一個產品用工廠方法,一系列產品,用抽象工廠

創建一個產品用工廠方法;

創建一系列產品用抽象工廠;

 

 

但是雖然解決了問題,還是有很多的問題的,單單從上邊我們創建了那么多的子類,以及子類必須重寫這一點來看,這么設計肯定有弊端,那有什么改進的呢?咱們繼續往下看。

 

 

 五、抽象工廠與依賴注入

這里我就不詳細說了,其實就是一個思路的用法,這里舉個例子就行了,大家肯定都用過三層架構,其中有一個數據訪問層 DALFactory ,我們平時使用的時候,就是直接把類的實例給 return 出來,如果我們同時連接多個數據庫呢?那這樣的話,我們就像上邊說到的,建立多個 DAL 層,比如 DALSqlServer、DALMysql 等等,那我們如何通過接口來獲取服務呢,就是通過反射指定的程序集來實現,這個就是簡單的使用了抽象工廠。

比如這個網上的圖片,就是這個意思,大家看個意思就行:

 

說到這里大家有沒有了解到一些小小的心得,似乎這個和有一個東西很像!對!就是我們平時使用的依賴注入。其實我們可以想一想,我們在服務注冊的時候,通過反射將多個服務注冊到容器里,然后我們再使用的時候,是容器通過接口別名,給我們找到指定的具體服務,甚至也實現了一個接口,多個服務的操作,這個就是工廠模式和依賴注入的小小的關系。

工廠模式和依賴容器的區別:

factory,di container,service locator都是對象的創建者或者提供者,為什么是不同的名字,不是因為它們提供對象的實現,而是取決於你如何使用。你通過注入的方編程,讓框架利用它提供對象,它就是di container,你顯式利用它來提供你所需要的對象,它就叫做service locator。

 

總結一下:

今天我們通過簡單的代碼,一步一步,從【簡單工廠】開始,了解到了多個類是如何創建的,然后明白了【工廠方法】模式,更好的實現了OCP原則,也實現了封裝多態的原理,接下來咱們通過【抽象工廠】的舉例,進一步對一系列一組產品生產的時候,所采用的方案,到最后,我們簡單的說明了一下反射以及依賴注入和工廠模式的關系,可能讀起來還是有點兒凌亂,不過我還是簡單大家多多的學學,查查資料,因為我認為,設計模式是結構的基礎,而工廠模式又是設計模式的基礎,可見其重要性,如果看不懂沒關系,等我直播講課吧。 

 

當然抽象工廠也是有一些弊端的,比如(摘抄百度):

  【3.1】、優點:【抽象工廠】模式將系列產品的創建工作延遲到具體工廠的子類中,我們聲明工廠類變量的時候是使用的抽象類型,同理,我們使用產品類型也是抽象類型,這樣做就盡可能的可以減少客戶端代碼與具體產品類之間的依賴,從而降低了系統的耦合度。耦合度降低了,對於后期的維護和擴展就更有利,這也就是【抽象工廠】模式的優點所在。可能有人會說在Main方法里面(這里的代碼就是客戶端的使用方)還是會使用具體的工廠類,對的。這個其實我們通過Net的配置,把這部分移出去,最后把依賴關系放到配置文件中。如果有新的需求我們只需要修改配置文件,根本就不需要修改代碼了,讓客戶代碼更穩定。依賴關系肯定會存在,我們要做的就是降低依賴,想完全去除很難,也不現實。

  【3.2】、缺點:有優點肯定就有缺點,因為每種模式都有他的使用范圍,或者說要解決的問題,不能解決的問題就是缺點了,其實也不能叫缺點了。【抽象工廠】模式很難支持增加新產品的變化,這是因為抽象工廠接口中已經確定了可以被創建的產品集合,如果需要添加新產品,此時就必須去修改抽象工廠的接口,這樣就涉及到抽象工廠類的以及所有子類的改變,這樣也就違背了“開發——封閉”原則。

  【3.3】、抽象工廠模式的使用場景:   如果系統需要多套的代碼解決方案,並且每套的代碼方案中又有很多相互關聯的產品類型,並且在系統中我們可以相互替換的使用一套產品的時候可以使用該模式,客戶端不需要依賴具體實現。

 

 

 六、示例代碼

https://github.com/anjoy8/DesignPattern/tree/master/FactoryPattern

 


【參考文獻:】

1、Dependency Injection vs Factory Pattern

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM