IoC原理及實現


什么是IoC 

IoC是Inversion of Control的縮寫,翻譯過來為“控制反轉”。簡單來說,就是將對象的依賴關系交由第三方來控制。在理解這句話之前,我們先來回顧一下IoC的演化。

Ioc前世今生

傳統的new class的方式
我們寫了一個ChineseSpeaker的類,他有一個SayHello的方法並調用輸出控制台:

 class Program
        {
            static void Main(string[] args)
            {
                ChineseSpeaker chineseSpeaker= new ChineseSpeaker();
                chineseSpeaker.SayHello();
            }
        }


        public class ChineseSpeaker
        {
            public void SayHello()
            {
                Console.WriteLine("你好!!!");
            }
        }

上面看起來沒有任何問題,一切都很好,但是有一天英國演講者打招呼的話,我們就需要新建了一個BritishSpeaker類:

class Program
        {
            static void Main(string[] args)
            {
                //ChineseSpeaker chineseSpeaker = new ChineseSpeaker();
                //chineseSpeaker.SayHello();

                BritishSpeaker britishSpeaker = new BritishSpeaker();
                britishSpeaker.SayHello();
            }
        }

        public class BritishSpeaker
        {
            public void SayHello()
            {
                Console.WriteLine("Hello!!!");
            }
        }

        //ChineseSpeaker 同上面的代碼一樣
到目前為止,代碼已經暴露出設計原則中要避免的問題:松耦合(loose coupling)。程序中非常依賴實際的class,這導致了:
當出現“日本人”、“印度人”時,我們不得不修改和重新編譯代碼。當程序代碼和邏輯不復雜的時候問題不大,但當程序變大的時候程序猿就苦逼了。
Interface方式
為了避免這種直接依賴關系,我們需要把對象或行為抽象出來一個東西,叫做接口(interface)。它就像電腦中的usb插口,無論是優盤還是鼠標只要插頭是usb的我就能使用,從而屏蔽了復雜性。
因此,我們把代碼改成:
        public interface ISpeak
        {
            void SayHello();
        }

        public class BritishSpeaker : ISpeak
        {
            public void SayHello()
            {
                Console.WriteLine("Hello!!!");
            }
        }


        public class ChineseSpeaker : ISpeak
        {
            public void SayHello()
            {
                Console.WriteLine("你好!!!");
            }
        }
因為我們現在把類的實現和功能拆出來了,所以我們可以讓客戶端動態的來選擇誰SayHello
class Program 
       { 
           static void Main(string[] args) 
           { 
               //ChineseSpeaker chineseSpeaker = new ChineseSpeaker(); 
               //chineseSpeaker.SayHello(); 
               //BritishSpeaker britishSpeaker = new BritishSpeaker(); 
               //britishSpeaker.SayHello();

               ISpeak speak;

               if (args.Length > 0 && args[0] == "Chinese") 
               { 
                   speak = new ChineseSpeaker(); 
               } 
               else 
               { 
                   speak = new BritishSpeaker(); 
               }

               speak.SayHello(); 
           } 
       }

這時候我們不知不覺的用到了面向對象六大原則中的依賴倒轉原則(DIP),高層模塊不依賴於低層模塊的實現,而低層模塊依賴於高層模塊定義的接口。 好,讓我們回到IoC,比較上面的兩種寫法:

  • 傳統的寫法類在定義的瞬間就已經決定具體的類型,他的流程是從上到下的
  • 使用interface的寫法是在實例化時才決定類的具體類型,也就是用到的時候才會new(),他的流程是new后面來控制的

這時候我們再來看IoC的意思是控制反轉,就能大概理解了。傳統的寫法流程屬於從上到下,而interface寫法則是由new()其他的類來決定類的實現,因此控制的流程反轉了。

DI是什么

利用interface的方式,可以讓類在使用的時候再決定由哪個具體類來實現。那該如何實現這種方式呢?這時就有一個新的名稱出現了,就是Dependency Injection(依賴注入),簡稱DI。DI有三種方式,分別是構造函數注入、屬性注入、接口注入
構造函數注入

  public class Printer
        {
            private ISpeak _speak;
            public Printer(ISpeak speak)//構造函數注入
            {
                _speak = speak;
            }
        }

        class Program
        {
            static void Main(string[] args)
            {
                //ChineseSpeaker chineseSpeaker = new ChineseSpeaker();
                //chineseSpeaker.SayHello();
                //BritishSpeaker britishSpeaker = new BritishSpeaker();
                //britishSpeaker.SayHello();

                //ISpeak speak;

                //if (args.Length > 0 && args[0] == "Chinese")
                //{
                //    speak = new ChineseSpeaker();
                //}
                //else
                //{
                //    speak = new BritishSpeaker();
                //}

                //speak.SayHello();

                Printer print;

                if (args.Length > 0 && args[0] == "Chinese")
                {
                    print = new Printer(new ChineseSpeaker());
                }
                else
                {
                    print = new Printer(new BritishSpeaker());
                }

            }
        }


屬性注入

public class Printer
{
    public ISpeak Speaker { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        //ChineseSpeaker chineseSpeaker = new ChineseSpeaker();
        //chineseSpeaker.SayHello();
        //BritishSpeaker britishSpeaker = new BritishSpeaker();
        //britishSpeaker.SayHello();

        //ISpeak speak;

        //if (args.Length > 0 && args[0] == "Chinese")
        //{
        //    speak = new ChineseSpeaker();
        //}
        //else
        //{
        //    speak = new BritishSpeaker();
        //}

        //speak.SayHello();

        Printer print = new Printer();
        if (args.Length > 0 && args[0] == "Chinese")
        {
            print.Speaker = new ChineseSpeaker();
        }
        else
        {
            print.Speaker = new BritishSpeaker();
        }
    }
}

接口注入
    //接口注入
        public interface IPrint
        {
            void SetSpeaker(ISpeak speak);
        }

        public class Printer : IPrint
        {
            private ISpeak _speak;
            public void SetSpeaker(ISpeak speak)
            {
                _speak = speak;
            }
        }

        class Program
        {
            static void Main(string[] args)
            {
                //ChineseSpeaker chineseSpeaker = new ChineseSpeaker();
                //chineseSpeaker.SayHello();
                //BritishSpeaker britishSpeaker = new BritishSpeaker();
                //britishSpeaker.SayHello();

                ISpeak speak;

                if (args.Length > 0 && args[0] == "Chinese")
                {
                    speak = new ChineseSpeaker();
                }
                else
                {
                    speak = new BritishSpeaker();
                }

                Printer printer = new Printer();
                printer.SetSpeaker(speak);
            }
        }

IoC與DI的關系

我的理解是IoC是一種理念,DI則是它的具體實現方式

IoC Container

IoC Container幫我們在項目運行時動態的創建實例,它主要功能如下:

  • 動態創建、注入依賴對象
  • 管理對象生命周期
  • 映射依賴關系

IoC Container技術實現的原理就是“反射(Reflection)”。利用反射動態的創建對象,把依賴關系注入到指定對象中。一般常用的注入方式是構造函數注入和屬性注入

Service Locator模式

服務定位模式也是IoC理念的一種實現。實現原理:通過ServiceLocator類提供實現IServiceLocator接口的單例,並負責管理已注冊實例的創建和訪問。通常結合工廠模式來結合使用。
Service Locator與IoC Container都是IoC的具體實現方式。不同的是Service Locator沒有提供管理對象生命周期的功能

.NET 平台下的IoC Container框架

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

Castle Windsor:  http://www.castleproject.org/container/index.html

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

StructureMap: http://docs.structuremap.net/

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

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

結束語

我在學習IoC過程中,學以致用,自己模仿Nject實現了一個IoC Container框架,可以用FluentAPI和xml配置依賴關系,希望對大家有幫助。項目地址:https://github.com/Khadron/Peace


免責聲明!

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



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