IoC和DI的理解


1 概述

當我們想閉上眼睛想如何讓我們的軟件更加可用可維護時,我們總能想到一個詞:松耦合。在這篇文章中,主要講述了模塊間存在的依賴關系,但這種依賴關系違背了依賴倒置原則。在這之后,我們將討論一種解除軟件依賴關系的設計模式——IoC,以及它的兩種實現方法:依賴注入(DI)和服務定位。最后我們簡單地列下當前流行的IoC容器工具。

目錄

  • 依賴
  • 依賴倒置原則(DIP)
  • 控制反轉IoC:解除兩個模塊間的直接依賴關系
  • 依賴注入(DI)
  • 服務定位(Service Locator)
  • IoC容器

2 依賴

當一個模塊/類使用另一個模塊/類,即存在了一種依賴關系。

示例場景:

在這里,我們使用銷售系統中保存訂單的場景。比如,我們當前的需求是將數據保存到SQL Server中。在這里我們只關注業務層與數據持久層之間的依賴關系。

示例v1:

    public class SQLServerOrderDAL
    {
        public void Add() 
        {
            Console.WriteLine("The order was added in into sql server database.");
        }
    }

    public class OrderManager
    {
        public void AddOrder() 
        {
            SQLServerOrderDAL orderDAL = new SQLServerOrderDAL();
            orderDAL.Add();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            OrderManager orderManager = new OrderManager();
            orderManager.AddOrder();

            Console.ReadLine();
        }
    }
View Code

3 依賴倒置原則(DIP)

在這個時候,當公司的牛XX業務人員將這個銷售系統又賣給了另一家公司,大家都HAPPY。 但是對方公司唯一希望數據保存到ORACLE,因為他們其它的軟件已經購買了ORACLE授權,並且不想再購買SQL SERVER授權。即軟件需要支持數據庫更換,我們應該如何解決

依賴倒置原則(DIP)

高層模塊不應依賴於低層模塊,兩者應該依賴於抽象。

抽象不不應該依賴於實現,實現應該依賴於抽象。

 

上述示例v1中,OrderManager依賴於OrderDAL. 即高層模塊(OrderManager)直接依賴於實現(OrderDAL), 而不是依賴於抽象。下面我們為數據持久層添加一層抽象,讓業務層和數據持久實現層都依賴於抽象。

示例v2:

    public interface IOrderDAL
    {
        void Add();
    }

    public class OracleOrderDAL:IOrderDAL
    {
        public void Add()
        {
            Console.WriteLine("The order was added in into oracle database.");
        }
    }

    public class SQLServerOrderDAL:IOrderDAL
    {
        public void Add()
        {
            Console.WriteLine("The order was added in into sql server database.");
        }
    }

    public class OrderManager
    {
        public void AddOrder() 
        {
            IOrderDAL orderDAL = new OracleOrderDAL();
            orderDAL.Add();
        }
    }

class Program
    {
        static void Main(string[] args)
        {
            OrderManager orderManager = new OrderManager();
            orderManager.AddOrder();

            Console.ReadLine();
        }
    }
View Code

 

4 控制反轉IoC

程序到這里,數據訪問層已經添加一層抽象,即業務層和數據持久層都依賴於抽象。但是,注意這里,依然存在都依賴,即業務層依然要直接依賴於低層的數據持久實現層。當更換數據庫時,會有很多這樣的代碼要更換。你知道我在說什么的..這還是太惡心…

文章到這里了,我們先說說控制反轉——IoC(Inversion of Control), 網上關於IoC的解釋很多,我比較喜歡這種http://www.cnblogs.com/liuhaorain/p/3747470.html:

它為相互依賴的組件提供抽象,將依賴(低層模塊)對象的獲得交給第三方(系統)來控制即依賴對象不在被依賴模塊的類中直接通過new來獲取

 

在多數情況下,上面的解釋已經夠用了,更廣義一些的解釋我認為是:

相對於過程式編程中,代碼按順序一步步執行,控制反轉強調的是,將控制權交出,讓第三方來控制程序的執行。

即除了將對象創建的工作交給第三方(我們主要關心的,如依賴注入和服務定位),IoC應該還包括將流程的控制轉交出去,如事件,Callback 委托,觀察者模式,異步等

在接下來的文章中,我們主要討論依賴注入和服務定位。

5 依賴注入(DI)

依賴注入DI——Dependence Injection, 是一種IoC的實現方式。即將依賴對象的創建和綁定工作轉移到第三者(調用方)。即 如果A對象依賴於B或C對象,那么就將B或C對象的創建工作轉到A對象的調用方去。在上面示例中,將OracleOrderDAL或SQLServerOrderDAL的創建工作和綁定到IOrderDAL的工作轉移到OrderManager的調用方中去,即具體對象創建的選擇權移交到更高層的調用方法中。

依賴注入又分構造函數注入,屬性注入和接口注入。我認為接口注入並不好用,所以這里只做構造函數和屬性注入的示例。

5.1 構造函數注入

就是通過構造函數,傳遞依賴項。在這里,通過OrderManager的構造函數傳入SQLServerOrderDAL或OracleOrderDAL

示例v3

    public class OrderManager
    {
        private IOrderDAL _orderDAL;

        public OrderManager(IOrderDAL orderDAL)
        {
            this._orderDAL = orderDAL;
        }

        public void AddOrder()
        {

            this._orderDAL.Add();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IOrderDAL orderDAL = new SQLServerOrderDAL();
            //IOrderDAL orderDAL = new OracleOrderDAL();
           
            OrderManager orderManager = new OrderManager(orderDAL);
            orderManager.AddOrder();

            Console.ReadLine();
        }
    }
View Code

5.2 屬性注入

即通過屬性傳入依賴項。在這里,能過OrderManager的OrderDA屬性項,傳入SQLServerOrderDAL或OracleOrderDAL

示例4

    public class OrderManager
    {
        private IOrderDAL _orderDAL;

        public IOrderDAL OrderDAL 
        {
            set { this._orderDAL = value; }
            get { return this._orderDAL; }
        }

        public void AddOrder() 
        {

            this._orderDAL.Add();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IOrderDAL orderDAL = new SQLServerOrderDAL();
            //IOrderDAL orderDAL = new OracleOrderDAL();
           
            OrderManager orderManager = new OrderManager();
            orderManager.OrderDAL = orderDAL;
            orderManager.AddOrder();

            Console.ReadLine();
        }
    }
View Code

6 服務定位(Service Locator)

服務定位SL——Service Locator, 也是一種IoC的實現方式。即將依賴對象的創建和綁定工作轉移到第三者(另一個模塊)。即 如果A對象依賴於B或C對象,那么就將B或C對象的創建工作轉到F對象中去。在上面示例中,將OracleOrderDAL或SQLServerOrderDAL的創建工作和綁定到IOrderDAL的工作轉移到Factory中去, 即具體對象的創建選擇權移交給Factory中。

示例v5

    public class Factory
    {
        public static IOrderDAL GetOrderDAL() 
        {
            //string type = "SQLServer";
            string type = "Oracle";
            string assemblyName = string.Format("v5_{0}DAL",type);
            string typeName = string.Format("{0}.{1}OrderDAL",assemblyName,type);
            return (IOrderDAL)Assembly.Load(assemblyName).CreateInstance(typeName);
        }
    }

    public class OrderManager
    {
        public void AddOrder() 
        {
            IOrderDAL orderDAL = Factory.GetOrderDAL();
            orderDAL.Add();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            OrderManager orderManager = new OrderManager();
            orderManager.AddOrder();

            Console.ReadLine();
        }
    }
View Code

7 IoC容器

下面是一些主流的IoC容器工具以供參考

1. Unity:  http://unity.codeplex.com/

2. Ninject:  http://www.ninject.org/

3. Autofac:  http://code.google.com/p/autofac/

4. Spring.NET: http://www.springframework.net/

Unity示例v6

    public class OrderManager
    {
        private IOrderDAL _orderDAL;

        public OrderManager(IOrderDAL orderDAL)
        {
            this._orderDAL = orderDAL;
        }

        public void AddOrder()
        {

            this._orderDAL.Add();
        }
    }

    class Program
    {
        
        static void Main(string[] args)
        {
            UnityContainer container = new UnityContainer();
            //container.RegisterType<IOrderDAL, SQLServerOrderDAL>();
            container.RegisterType<IOrderDAL, OracleOrderDAL>();

            OrderManager orderManager = container.Resolve<OrderManager>();
            orderManager.AddOrder();

            Console.ReadLine();
        }
    }
View Code

上面是一些我關於IoC和DI的理解,如有不對的地方,歡迎提出討論。

 

相關鏈接:

Inversion of Control Containers and the Dependency Injection pattern

Inversion of Control and Dependency Injection: Working with Windsor Container

Dependency Injection (DI) vs. Inversion of Control (IOC)
Inversion of Control – An Introduction with Examples in .NET

Understanding Inversion of Control, Dependency Injection and Service Locator

Dependency Injection

Dependency Inversion Principle, IoC Container, and Dependency Injection

Unity Container Introduced by MSDN


免責聲明!

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



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