淺談IOC


一、引言

IOC-Invertion of Control,即控制反轉,是一種程序設計思想,世上本沒有路,走的人多了便有了路,本文將一步步帶你了解IOC設計思想的演進之路。

在學習IOC之前我們先初步了解幾個概念

依賴(Dependency):就是有聯系,表示一個類依賴於另一個類

依賴倒置原則(DIP):設計模式六大原則之一,是一種軟件架構設計原則

控制反轉(IOC):一種軟件設計原則,上層對下層的依賴(即底層模塊的獲得)交給第三方

依賴注入(DI):實現IOC的一種方式、手段

IOC容器:依賴注入的框架,用來映射依賴,管理對象創建和生存周期

二、依賴

依賴就是有聯系,有地方使用它就是有依賴它,下面看一個簡單的示例

  class BMW
    {
        public string Show()
        {
            return "寶馬";
        }
    }
    class ChinesePeople
    {
        private BMW bmw = new BMW();
        public void Run()
        {
            Console.WriteLine($"今天開{bmw.Show()}上班");
        }
    }
     class Program
    {
        static void Main(string[] args)
        {
            ChinesePeople people = new ChinesePeople();
            BMW bmw = new BMW();
            people.Run();
            Console.Read();
        }
    }
View Code

上面中國人開着寶馬去上班,客戶端有使用中國人、寶馬汽車兩個對象,中國人中有使用對象寶馬汽車,我們可以從中找到三個依賴關系:

客戶端依賴對象ChinesePeople;

客戶端依賴對象BMW;

ChinesePeople依賴對象BMW;

三、依賴倒置原則

過些日子來了新需求,中國人不僅要開寶馬去上班,還要開奔馳去上班,如果按照上面直接依賴關系的方式去做,我們就需要修改ChinesePeople類,讓它實現一個參數為寶馬的重載方法Run(),顯然這樣不是好的設計,我們總不能每次新增一種汽車(即修改下層模塊)都要去修改ChinesePeople類吧(相對於汽車為上層模塊),太麻煩了。。。

先簡單分析一下,耦合關系就是依賴關系,如果依賴關系很重,牽一發而動全身,將很難維護擴展,耦合關系越少,系統會越穩定,因此要較少依賴

定義:A.高層模塊不應依賴於底層模塊,兩者應該依賴於抽象

   B.抽象不應該依賴於細節,細節應該依賴於抽象

在這個圖中,我們發現高層模塊定義接口,將不直接依賴於下層模塊,下層模塊負責實現高層模塊定義的接口,下面看代碼demo:

    interface ICar
    {
        string Show();
    }
    class BMW:ICar
    {
        public string Show()
        {
            return "寶馬";
        }
    }
     class BenZ : ICar
    {
        public string Show()
        {
            return "奔馳";
        }
    }
    interface IPeople
    {
         void Run(ICar bmw);
    }
    class ChinesePeople :IPeople
    {
        public void Run(ICar bmw)
        {
            Console.WriteLine($"今天開{bmw.Show()}上班");
        }
    }
     class Program
    {
        static void Main(string[] args)
        {
            ICar carBMW = new BMW();
            ICar carBenZ = new BenZ();
            IPeople people = new ChinesePeople();
            people.Run(carBMW);
            people.Run(carBenZ);
            Console.Read();
        }
    }
View Code

分析:上面代碼中,ChinesePeople類不再依賴於具體的汽車,而是依賴於汽車的抽象,這樣使得不管換什么樣的汽車品牌,中國人都是可以開着去上班的,而且不需要修改ChinesePeople類。想一下,這樣是不是挺好的,我們可以得出:上層不再依賴細節,相比面向實現,面向接口較好,因為抽象相比細節要更穩定。

四、控制反轉

上面示例中,我們實現了具體的人和具體的汽車的隔離,具體人只和汽車的接口有關。但是Program中main方法里的具體對象寫死了,控制權變小,當我要修改美國人開着福特去上班時,就不得不要去修改代碼,那怎么把控制權轉移呢?

下面看一個簡單的示例:

    interface ICar
    {
        string Show();
    }
    class BMW:ICar
    {
        public string Show()
        {
            return "寶馬";
        }
    }
    interface IPeople
    {
         void Run(ICar bmw);
    }
    class ChinesePeople :IPeople
    {
        public void Run(ICar bmw)
        {
            Console.WriteLine($"今天開{bmw.Show()}上班");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            string people =     ConfigurationManager.AppSettings["people"];
            string car = ConfigurationManager.AppSettings["car"];
            Assembly assemblypeople = Assembly.Load(people.Split(',')[1]);
            Assembly assemblycar = Assembly.Load(car.Split(',')[1]);
            Type typepeople = assemblypeople.GetType(people.Split(',')[0]);
            Type typecar = assemblypeople.GetType(car.Split(',')[0]);
            IPeople ipeople= (IPeople)Activator.CreateInstance(typepeople);
            ICar icar = (ICar)Activator.CreateInstance(typecar);
            ipeople.Run(icar);
            Console.Read();
        }
    }

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
  <appSettings>
    <add key="people" value="MyIOC_IOC.ChinesePeople,MyIOC_IOC"/>
    <add key="car" value="MyIOC_IOC.BMW,MyIOC_IOC"/>
  </appSettings>
</configuration>
View Code

上面代碼中,我們使用反射+配置文件的方式,將對象創建的控制權轉移到了配置文件,這就是所謂的控制反轉

分析,控制反轉是將對象創建的控制權交給了第三方,可以是IOC容器,它就相當於工廠,我們要什么對象,工廠給我們什么對象,這樣依賴關系就變了,它們(人和車)都依賴於IOC容器,通過IOC容器建立它們之間的依賴關系。(依賴對象不再被依賴模塊的類中直接通過new來獲取)

五、依賴注入

上面說到的控制反轉,我們了解到是將控制權轉移,這是我們的目的,配置文件+反射是是一種實現,而依賴注入則提供的是一種思想,或者說是實現IOC的手段。

依賴注入是將對象的創建和綁定轉移到被依賴對象的外部來實現。在依賴關系中ChinesePeople類所依賴的對象BMW類的創建和綁定是在ChinesePeople類內部執行的,顯然這種方法是不可取的,那我們怎么BMW類的引用傳遞給ChinesePeople類呢?

方法一 構造函數注入

     interface ICar
    {
        string Show();
    } 
    class BMW:ICar
    {
        public string Show()
        {
            return "寶馬";
        }
    }
     class ChinesePeopleContructor 
    {
        private ICar _car;
        public ChinesePeopleContructor(ICar bmw)
        {
            _car = bmw;
        }
        public void Run()
        {
            Console.WriteLine($"今天開{_car.Show()}上班");
        }
    }
    static void Main(string[] args)
        {
            ICar car = new BMW();
            ChinesePeopleContructor people = new ChinesePeopleContructor(car);
            people.Run();
            Console.Read();
        }
View Code

分析,BMW類對象的創建和綁定轉移到ChinesePeople類的外部來實現,解除了兩個對象之間的耦合,當需要開奔馳去上班的時候,只需要定義一個奔馳類,外部重新綁定依賴,不需要修改ChinesePeople類的內部,即可是先中國人開奔馳去上班的需求

方法二 屬性注入

interface ICar
    {
        string Show();
    }
    class BMW:ICar
    {
        public string Show()
        {
            return "寶馬";
        }
    }
    class ChinesePeopleProperty
    {
        private ICar _ICar;
        public ICar IC
        {
            get { return _ICar; }
            set { _ICar = value; }       
        }
        public void Run()
        {
            Console.WriteLine($"今天開{_ICar.Show()}上班");
        }
    }
    static void Main(string[] args)
     {
         ICar car = new BMW();
         ChinesePeopleProperty people = new ChinesePeopleProperty();
         people.IC = car;
         people.Run();
         Console.Read();
     }
View Code

分析,屬性注入是通過給屬性賦值,從而傳遞依賴

方法三 接口注入

     interface ICar
    {
        string Show();
    }
     class BMW:ICar
    {
        public string Show()
        {
            return "寶馬";
        }
    }
     interface IDependent
    {
        void SetDependent(ICar icar);
    }
    class ChinesePeopleInterface : IDependent
    {
        private ICar _ICar;
        public void SetDependent(ICar icar)
        {
            _ICar = icar;
        }
        public void Run()
        {
            Console.WriteLine($"今天開{_ICar.Show()}上班");
        }
    }
        static void Main(string[] args)
        {        
              ICar car = new BMW();
              ChinesePeopleInterface people = new ChinesePeopleInterface();
              people.SetDependent(car);
              people.Run();
              Console.Read();    
        }      
View Code

分析,接口依賴是定義一個設置依賴的方法,然后被依賴類繼承並實現這個接口

六、IOC容器

IOC容器是一個DI框架,主要功能有一下幾點

1.動態創建、注入依賴對象;

2.管理對象生命周期

2.映射依賴關系

常見的IOC容器:Spring.NET,Castle Windsor, Ninject,Autofac,Unity等等。。。

ioc容器提供了很多豐富的API,由於時間和篇幅等關系,我會在下篇博客中和您一起學習IOC容器之一Unity,敬請期待,未完待續。。。

 

不努力一把,坐井觀天,將永遠不知道自己和別人的差距有多大,身為菜鳥的我相信,天道酬勤,大道至簡,最好的成功之道便是堅持、學習、總結。

本文版權歸作者和博客園共有,歡迎轉載,轉載請注明出處。感謝您的閱讀。

 


免責聲明!

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



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