[.net 面向對象程序設計深刻](31)實戰設計模式——使用IoC模式(控制反轉或依賴注入)實現鬆散耦合設計(1)
html
先看一些名詞含義:git
IOC: Inversion of control 控制反轉,簡稱github
DI: Dependency Injection 依賴注入,簡稱spring
DIP: 依賴倒置原則 一種軟件架構設計的原則(抽象概念),「設計模式使用場景及原則」一篇中介紹過設計模式的幾種原則之一。數據庫
IoC容器:依賴注入的框架,用來映射依賴,管理對象建立和生存週期(DI框架)。編程
(1)IOC和DI,是站在不一樣角度的描述,自己是同一個概念設計模式
先說說這兩個對於初次接觸到的同窗難以理解的概念,首先這兩個東東是一回事,只是由於角度不一樣,有了兩個名字。瀏覽器
舉個不太恰當的例子,好比一我的,爸爸叫你兒子,爺爺叫你孫子,那這個兒子和孫子都是你,是同一我的,只是站的角度不一樣,這麼說容易理解了吧。架構
依賴注入(DI)是從應用程序的角度在描述,能夠把依賴注入描述完整點:應用程序依賴容器建立並注入它所須要的外部資源;框架
而控制反轉(IOC)是從容器的角度在描述,描述完整點:容器控制應用程序,由容器反向的嚮應用程序注入應用程序所須要的外部資源。
(2)是一種大粒度的設計模式
和GOF的23種設計模式相比較。IOC模式(一般中文稱"依賴注入"或「依賴倒置」),因爲出現時間比較晚,沒有被收錄。
其次和23種設計模式相比較,它的粒度更大一些,和MVC模式同樣,一般到架構層面了,而不是具體代碼片斷級別。理解到這裏就能夠了。
但它仍然是設計模式,只是和23種設計模式比,23種模式是戰術層面,IOC模式是戰略層面的。
依賴注入映射到面向對象程序開發中就是:高層類應該依賴底層基礎設施來提供必要的服務。
編寫鬆耦合的代碼提及來很簡單,可是實際上寫着寫着就變成了緊耦合。
一個比較難理解的正式定義以下:
依賴注入(Dependency Injection),是這樣一個過程:因爲某客戶類只依賴於服務類的一個接口,而不依賴於具體服務類,因此客戶類只定義一個注入點。
在程序運行過程當中,客戶類不直接實例化具體服務類實例,而是客戶類的運行上下文環境或專門組件負責實例化服務類,而後將其注入到客戶類中,保證客戶類的正常運行。
下面的說明比較容易理解:
理解DI的關鍵是:「誰依賴誰,爲何須要依賴,誰注入誰,注入了什麼」,那咱們來深刻分析一下:
●誰依賴於誰:固然是應用程序依賴於IoC容器;
●爲何須要依賴:應用程序須要IoC容器來提供對象須要的外部資源;
●誰注入誰:很明顯是IoC容器注入應用程序某個對象,應用程序依賴的對象;
●注入了什麼:就是注入某個對象所須要的外部資源(包括對象、資源、常量數據)。
IoC和DI由什麼關係呢?其實它們是同一個概念的不一樣角度描述,因爲控制反轉概念比較含糊(可能只是理解爲容器控制對象這一個層面,很難讓人想到誰來維護對象關係),
因此2004年大師級人物Martin Fowler又給出了一個新的名字:「依賴注入」,相對IoC 而言,「依賴注入」明確描述了「被注入對象依賴IoC容器配置依賴對象」。
依賴注入不是目的,它是一系列工具和手段。
最終的目的是幫助咱們開發出鬆散耦合(loose coupled)、可維護、可測試的代碼和程序。
這條原則的作法是你們熟知的面向接口,或者說是面向抽象編程。
常見的三層架構中,雖然表面上表現、業務、數據分層設計。但在數據庫層每每會產生一些與具體業務有關的類,並且若是不嚴格遵循代碼規範,會致使產生表現層直接new數據層的狀況。
若是要換個數據源呢?假如不使用ADO.NET等試,改成Http呢?這將是領域邏輯層和表現層與之耦合的代碼要進行大量更動。
這樣使得整個系統緊耦合,而且可測試性差。
在系統設計過程當中,各個類從上層到下層類之間必然會產生耦合,若是徹底沒有耦合,那麼這個類或程序集就能夠從項目中移除了。
所以如何使之達到鬆散耦合,從而提升可測試性呢?依賴注入將能很好的解決上述問題。
下面以一個簡單購物過程爲例來講明IOC模式如何實現鬆散耦合。
(1)傳統三層模式
代碼以下:
public class DalSqlServer { public void Add() { Console.WriteLine("在數據庫中添加一條訂單!"); } }
public class Order { private readonly DalSqlServer dal = new DalSqlServer();//添加一個私有變量保存數據庫操做的對象 public void Add() { dal.Add(); } }
class Program { static void Main(string[] args) { Order order = new Order(); order.Add(); Console.Read(); } }
運行結果:
上面的代碼看着功能都實現了,然而忽然老闆說,SqlServer要花錢買,咱們使用免費的Access吧,好吧,那就改改嘍。改動後以下:
這時,咱們只能再增長一個DalAcess類,來解決,代碼以下:
public class DalAccess { public void Add() { Console.WriteLine("在Access數據庫中添加一條訂單!"); } }
public class Order { private readonly DalAccess dal = new DalAccess();//添加一個私有變量保存Access數據庫操做的對象 public void Add() { dal.Add(); } }
class Program { static void Main(string[] args) { Order order = new Order(); order.Add(); Console.Read(); } }
運行結果:
正在這時,老闆來了,說最近生意好,訂單劇增,改爲MySql數據庫吧,矇蔽了吧,示例代碼中只有一個Add()的方法,可實際項目中,不知道有多少工做量了。
(2)使用IOC模式改進
只因此在需求變化時,咱們的代碼改動量如此之大,是由於耦合,耦合,耦合。
前面說了耦合不可能不存在,若是不存在,那這個代碼就能夠從項目中移除了,可是要讓讓代碼可維護性強,就必須使用模式化的開發
固然依賴注入就能解決上述問題,依賴注入(DI),它提供一種機制,將須要依賴(低層模塊)對象的引用傳遞給被依賴(高層模塊)對象
咱們示例中低層模塊就是DalSqlServer,DalAccsess,DalMySql等,高層模塊就是Order.
那麼如何在Order內部不依賴DalSqlServer,DalAccsess,DalMySql的狀況下傳遞呢,
這就須要對DalSqlServer,DalAccsess,DalMySql進行抽象設計,咱們設計一個IData數據接口對象,讓DalSqlServer,DalAccsess,DalMySql去具體實現它,
在傳遞過程當中,咱們只須要訂單處理Order和數據接口層IData耦合,這樣,即便數據庫再變化,但IData是穩定的。也不須要再改動Order中的代碼了。是否是很不錯的解耦呢?
改進後以下:
代碼以下:
public class DalOracle : IData { public void Add() { Console.WriteLine("在Oracle數據庫中添加一條訂單!"); } }
public class DalAccess : IData { public void Add() { Console.WriteLine("在Access數據庫中添加一條訂單!"); } }
public class DalMySql : IData { public void Add() { Console.WriteLine("在MySql數據庫中添加一條訂單!"); } }
public class DalSqlServer : IData { public void Add() { Console.WriteLine("在SqlServer數據庫中添加一條訂單!"); } }
定單處理類:
public class Order { private IData idata; //定義私有變量保存抽象出來的數據接口 /// <summary> /// 經過構造函數注入 /// </summary> /// <param name="iData"></param> public Order(IData iData) { this.idata = iData; //傳遞依賴 } public void Add() { idata.Add(); } }
展現:
class Program { static void Main(string[] args) { //定義空訂單 Order order=null; //使用SqlServer order = new Order(new DalSqlServer()); order.Add(); //使用Oracle order = new Order(new DalOracle()); order.Add(); //使用Accesss order = new Order(new DalAccess()); order.Add(); //使用MySql order = new Order(new DalMySql()); order.Add(); Console.Read(); } }
這樣就能夠隨意切換數據庫了,運行結果以下:
(3)更進一步改進,控制反轉
上面說到站在另外一個角度講,咱們把選擇數據庫的權限交給第三方,是否是能夠不用每次在建立訂單時都指定依賴對象(即具體數據類),也就是控制反轉。
針對上面的每次指定依賴對象的問題,處理的方式不少,最簡單的咱們能夠經過一個配置文件來指定所使用的具體數據庫,在傳遞時經過反射的方式來映射數據類。
這樣就就靈活多了。
(1)構造函數注入
上面示例就是這種方式
(2)屬性注入
public class Order { public IData Idata{get;set;} public void Add() { this.Idata.Add(); } }
class Program { static void Main(string[] args) { //定義空訂單 Order order=null; //使用SqlServer order = new Order(); order.Idata = new DalSqlServer(); order.Add(); //使用Oracle order = new Order(); order.Idata = new DalOracle(); order.Add(); //使用Accesss order = new Order(); order.Idata = new DalAccess(); order.Add(); //使用MySql order = new Order(); order.Idata = new DalMySql(); order.Add(); Console.Read(); } }
(3)方法注入
public class Order { private IData idata; //私有變量保存抽象接口 //經過Idata方法傳遞依賴 public void Idata(IData idata) { this.idata = idata; } public void Add() { this.idata.Add(); } }
class Program { static void Main(string[] args) { //定義空訂單 Order order=null; //使用SqlServer order = new Order(); order.Idata( new DalSqlServer()); order.Add(); //使用Oracle order = new Order(); order.Idata(new DalOracle()); order.Add(); //使用Accesss order = new Order(); order.Idata(new DalAccess()); order.Add(); //使用MySql order = new Order(); order.Idata(new DalMySql()); order.Add(); Console.Read(); } }
對於大型項目來講,相互依賴的組件比較多。若是還用手動的方式,本身來建立和注入依賴的話,顯然效率很低,並且每每還會出現不可控的場面。正因如此,IoC容器誕生了。IoC容器其實是一個DI框架,它能簡化咱們的工做量。它包含如下幾個功能:
動態建立、注入依賴對象。
管理對象生命週期。
映射依賴關係。
好比比較知名的「基於DDD的現代ASP.NET開發框架--ABP」使用Castle Windsor框架處理依賴注入。它是最成熟的DI框架之一。還有不少其餘的框架,如Unity,Ninject,StructureMap,Autofac等等。
下面是園友整理出來的一些經常使用的IOC容器及官網:
(1). Ninject: http://www.ninject.org/
(2). Castle Windsor: http://www.castleproject.org/container/index.html
(3). Autofac: http://code.google.com/p/autofac/
(4). StructureMap: http://docs.structuremap.net/
(5). Unity: http://unity.codeplex.com/
(6). MEF: http://msdn.microsoft.com/zh-cn/library/dd460648.aspx
(7). Spring.NET: http://www.springframework.net/
(8). LightInject: http://www.lightinject.net/ (推薦使用Chrome瀏覽器訪問)
(1)依賴注入,是一種結構型的設計模式,即IOC模式。
(2)IOC意思爲控制反轉和依賴注入是同一律唸的不一樣角度的說法。
(3)依賴注入是讓咱們的應用程序依賴於抽象出來的服務類的接口,而不是具體的服務類,從而在具體的服務類發生需求變化時,咱們注入新的服務接口,作到鬆散耦合。
(4)依賴注入有三種簡單的方式,即構造函數注入,屬性注入,方法注入。
(5)在大型項目中爲了解決手動建立注入的效率低下,誕生了IOC容器,常見的有:Unity、Ninject、StructureMap、Autofac、Spring.NET等。
https://github.com/yubinfeng/BlogExamples.git
==============================================================================================
<若是對你有幫助,記得點一下推薦哦,若有有不明白或錯誤之處,請多交流>
<對本系列文章閱讀有困難的朋友,請先看 《.net 面向對象編程基礎》 和 《.net 面向對象程序設計進階》 >
<轉載聲明:技術須要共享精神,歡迎轉載本博客中的文章,但請註明版權及URL>
==============================================================================================