[.net 面向對象程序設計深刻](31)實戰設計模式——使用Ioc模式(控制反轉或依賴注入)實現鬆散耦合設計(1)

[.net 面向對象程序設計深刻](31)實戰設計模式——使用IoC模式(控制反轉或依賴注入)實現鬆散耦合設計(1)
html

1,關於IOC模式 

先看一些名詞含義: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容器配置依賴對象」。 

2,依賴注入的做用

依賴注入不是目的,它是一系列工具和手段。

最終的目的是幫助咱們開發出鬆散耦合(loose coupled)、可維護、可測試的代碼和程序。

這條原則的作法是你們熟知的面向接口,或者說是面向抽象編程。 

常見的三層架構中,雖然表面上表現、業務、數據分層設計。但在數據庫層每每會產生一些與具體業務有關的類,並且若是不嚴格遵循代碼規範,會致使產生表現層直接new數據層的狀況。

若是要換個數據源呢?假如不使用ADO.NET等試,改成Http呢?這將是領域邏輯層和表現層與之耦合的代碼要進行大量更動。 

這樣使得整個系統緊耦合,而且可測試性差。

在系統設計過程當中,各個類從上層到下層類之間必然會產生耦合,若是徹底沒有耦合,那麼這個類或程序集就能夠從項目中移除了。

所以如何使之達到鬆散耦合,從而提升可測試性呢?依賴注入將能很好的解決上述問題。 

3,IOC模式應用示例

下面以一個簡單購物過程爲例來講明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)更進一步改進,控制反轉

上面說到站在另外一個角度講,咱們把選擇數據庫的權限交給第三方,是否是能夠不用每次在建立訂單時都指定依賴對象(即具體數據類),也就是控制反轉。

針對上面的每次指定依賴對象的問題,處理的方式不少,最簡單的咱們能夠經過一個配置文件來指定所使用的具體數據庫,在傳遞時經過反射的方式來映射數據類。

這樣就就靈活多了。

4,IOC注入的幾種方式

 (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();
        }
    }

5,IOC容器(或DI框架) 

對於大型項目來講,相互依賴的組件比較多。若是還用手動的方式,本身來建立和注入依賴的話,顯然效率很低,並且每每還會出現不可控的場面。正因如此,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瀏覽器訪問)

6,總結

(1)依賴注入,是一種結構型的設計模式,即IOC模式。

(2)IOC意思爲控制反轉和依賴注入是同一律唸的不一樣角度的說法。

(3)依賴注入是讓咱們的應用程序依賴於抽象出來的服務類的接口,而不是具體的服務類,從而在具體的服務類發生需求變化時,咱們注入新的服務接口,作到鬆散耦合。

(4)依賴注入有三種簡單的方式,即構造函數注入,屬性注入,方法注入。

(5)在大型項目中爲了解決手動建立注入的效率低下,誕生了IOC容器,常見的有:Unity、Ninject、StructureMap、Autofac、Spring.NET等。

7,源代碼

https://github.com/yubinfeng/BlogExamples.git

==============================================================================================

返回目錄

<若是對你有幫助,記得點一下推薦哦,若有有不明白或錯誤之處,請多交流>

<對本系列文章閱讀有困難的朋友,請先看 《.net 面向對象編程基礎》 和 《.net 面向對象程序設計進階》 >

<轉載聲明:技術須要共享精神,歡迎轉載本博客中的文章,但請註明版權及URL>

.NET 技術交流羣:467189533 H.NET 技術交流羣

==============================================================================================

相關文章
相關標籤/搜索