身边的设计模式(三):抽象工厂 与 依赖注入
上篇文章,我們說到了簡單工廠和工廠方法,如果沒看過的,請先看上篇,不然的話,可能有些吃力,或者直接點擊閱讀原文,查看我博客園的對應詳細版的文章。
大家學到了這里,我建議自己可以練習練習,可以寫一下部分代碼,很好的區分下【簡單工廠】和【工廠方法】之間的區別和練習,如果說你已經看懂了,或者說自己已經練習好了,那咱們就繼續往下說今天的重要內容?—— 抽象工廠。
?
再說抽象工廠之前呢,咱們先簡單總結一下:
1、我們知道,工廠模式屬于創建型開發模式的一元,他的作用就是創建我們需要的對象,如果一個一個創建的話,會很麻煩,所以我們誕生出來了一個【簡單工廠】,這個簡單工廠只是簡單的人為的把幾個對象的實例給堆起來,通過type 來區分,然后分別 new 實例化,有時候也是一個很好的方案,但是這樣有一個弊端,違背了我們開發六大原則中的——OCP開放關閉原則,所以這個時候,我們就又多出來一個新的概念【工廠方法】。
2、【工廠方法】是在【簡單工廠】的基礎上,做了相應的改良——通過多個子類的形式,來替換之前的 type 分類法,對內修改關閉,對外新增打開,這樣無論是代碼整潔上,還是擴展封裝上,都有了很好的體驗,同時也滿足了我們的OCP開發原則。
3、但是!這種方案真的就很好了么,我們再來回憶一下,我們無論是簡單工廠,還是工廠方法,都是生成的單獨的一個類,好處是可以一一的慢慢擴展,但歸根結底還是在處理一個類,我們平時開發的時候,處理一個類是很多,比如常見的helper,Log 之類的。但是這不是我們開發的重點,我們平時使用最多的還是 Service 類,或者 Repository 類,里邊有很多,各種各樣的的類,比如 User 表,Role 表,Permission 表等等,每一個實體又都對應各自的一個服務類或者倉儲類。
4、那這個時候,我們使用上邊的【工廠方法】還行么?肯定是不行的!因為我們上邊是一個二維體系,EFCoreRepository 、SugarRepository、DapperRepository等等,是這樣的二維,我們現在要做的就是在這個二維的基礎上,再加上一個維度,就是要解決 User 、Role、Permission 實體等等這種一組或者一系列的類創建的問題。
5、那就是今天下邊要說到的【抽象工廠】模式。
?
注意,下邊的例子可能不太恰當,只是作為理解抽象工廠模式來使用,具體開發中,可能有出入。
這篇文章內容不是很全,我在博客園重新編排了一下,然后也同時加上了一個小故事,更方便理解,大家可以點擊【閱讀原文】來查看。
?
?一、抽象工廠模式
?
上邊的問題我們都看到了,我們要解決一系列一組類創建的問題,引申出來了抽象工廠模式,那下邊我們就簡單寫一些代碼,看看是否跑的通。
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();}
?
結果我就不調試,肯定是沒有問題的,畢竟僅僅是類的調用嘛,如果說你從上邊往下看,看到了這里,還沒有問題,并且能大概明白其中的意義,那你的工廠模式已經完全學會了,特別是這個抽象工廠,一直很繞,而且也使用的不是很直觀,用途不是很多。
?
現在我們再簡單的說明一下,我們通過【抽象工廠】模式,慢慢的明白了,其實抽象工廠是在【工廠方法】模式的基礎上,往外又多做了一套封裝,目的就是解決生產一系列產品的時候,工廠方法無法滿足的問題。
所以這個時候,如果有人問你二者的區別,核心的區別就是:如果是一個產品用工廠方法,一系列產品,用抽象工廠。
?
但是雖然解決了問題,還是有很多的問題的,單單從上邊我們創建了那么多的子類,以及子類必須重寫這一點來看,這么設計肯定有弊端,那有什么改進的呢?咱們繼續往下看
?
?
?二、抽象工廠與依賴注入
這里我就不詳細說了,其實就是一個思路的用法,這里舉個例子就行了,大家肯定都用過三層架構,其中有一個數據訪問層 DALFactory ,我們平時使用的時候,就是直接把類的實例給 return 出來,如果我們同時連接多個數據庫呢?那這樣的話,我們就像上邊說到的,建立多個 DAL 層,比如 DALSqlServer、DALMysql 等等,那我們如何通過接口來獲取服務呢,就是通過反射指定的程序集來實現,這個就是簡單的使用了抽象工廠。
比如這個網上的圖片,就是這個意思,大家看個意思就行:
?
?
說到這里大家有沒有了解到一些小小的心得,似乎這個和有一個東西很像!對!就是我們平時使用的依賴注入。其實我們可以想一想,我們在服務注冊的時候,通過反射將多個服務注冊到容器里,然后我們再使用的時候,是容器通過接口別名,給我們找到指定的具體服務,甚至也實現了一個接口,多個服務的操作,這個就是工廠模式和依賴注入的小小的關系。
?
總結一下:
今天我們通過簡單的代碼,一步一步,從【簡單工廠】開始,了解到了多個類是如何創建的,然后明白了【工廠方法】模式,更好的實現了OCP原則,也實現了封裝多態的原理,接下來咱們通過【抽象工廠】的舉例,進一步對一系列一組產品生產的時候,所采用的方案,到最后,我們簡單的說明了一下反射以及依賴注入和工廠模式的關系,可能讀起來還是有點兒凌亂,不過我還是簡單大家多多的學學,查查資料,因為我認為,設計模式是結構的基礎,而工廠模式又是設計模式的基礎,可見其重要性,如果看不懂沒關系,等我直播講課吧。?
當然抽象工廠也是有一些弊端的,比如:
? ??3.1】、抽象工廠模式的優點:【抽象工廠】模式將系列產品的創建工作延遲到具體工廠的子類中,我們聲明工廠類變量的時候是使用的抽象類型,同理,我們使用產品類型也是抽象類型,這樣做就盡可能的可以減少客戶端代碼與具體產品類之間的依賴,從而降低了系統的耦合度。耦合度降低了,對于后期的維護和擴展就更有利,這也就是【抽象工廠】模式的優點所在。可能有人會說在Main方法里面(這里的代碼就是客戶端的使用方)還是會使用具體的工廠類,對的。這個其實我們通過Net的配置,把這部分移出去,最后把依賴關系放到配置文件中。如果有新的需求我們只需要修改配置文件,根本就不需要修改代碼了,讓客戶代碼更穩定。依賴關系肯定會存在,我們要做的就是降低依賴,想完全去除很難,也不現實。
???3.2】、抽象工廠模式的缺點:有優點肯定就有缺點,因為每種模式都有他的使用范圍,或者說要解決的問題,不能解決的問題就是缺點了,其實也不能叫缺點了。【抽象工廠】模式很難支持增加新產品的變化,這是因為抽象工廠接口中已經確定了可以被創建的產品集合,如果需要添加新產品,此時就必須去修改抽象工廠的接口,這樣就涉及到抽象工廠類的以及所有子類的改變,這樣也就違背了“開發——封閉”原則。
???3.3】、抽象工廠模式的使用場景:???如果系統需要多套的代碼解決方案,并且每套的代碼方案中又有很多相互關聯的產品類型,并且在系統中我們可以相互替換的使用一套產品的時候可以使用該模式,客戶端不需要依賴具體實現。
?
?
?三、示例代碼
https://github.com/anjoy8/DesignPattern/tree/master/FactoryPattern
?
【參考文獻:】
1、Dependency Injection vs Factory Pattern
總結
以上是生活随笔為你收集整理的身边的设计模式(三):抽象工厂 与 依赖注入的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: asp.net core 从 3.0 到
- 下一篇: 边缘计算与云计算的不同,这篇说明白了!