生活随笔
收集整理的這篇文章主要介紹了
理解依赖注入(IOC)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
IOC:英文全稱:Inversion of Control,中文名稱:控制反轉(zhuǎn),它還有個名字叫依賴注入(Dependency Injection)。 作用:將各層的對象以松耦合的方式組織在一起,解耦,各層對象的調(diào)用完全面向接口。當系統(tǒng)重構(gòu)的時候,代碼的改寫量將大大減少。 理解依賴注入: ? ? 當一個類的實例需要另一個類的實例協(xié)助時,在傳統(tǒng)的程序設(shè)計過程中,通常有調(diào)用者來創(chuàng)建被調(diào)用者的實例。然而采用依賴注入的方式,創(chuàng)建被調(diào)用者的工作不再由調(diào)用者來完成,因此叫控制反轉(zhuǎn),創(chuàng)建被調(diào)用者的實例的工作由IOC容器來完成,然后注入調(diào)用者,因此也稱為依賴注入。 舉個有意思的例子(來源于互聯(lián)網(wǎng))
假如我們要設(shè)計一個Girl和一個Boy類,其中Girl有Kiss方法,即Girl想要Kiss一個Boy,首先問題是Girl如何認識Boy?
在我們中國常見的MM認識GG的方式有以下幾種:
A 青梅竹馬 ? ?B 親友介紹 ? C 父母包辦
哪一種是最好的?
1.青梅竹馬:很久很久以前,有個有錢的地主家的一閨女叫Lily,她老爸把她許配給縣太爺?shù)膬鹤覬immy,屬于指腹為婚,Lily非常喜歡kiss,但是只能kiss Jimmy
public?class?Lily{?? ????????public?Jimmy?jimmy;??? ????????public?Girl()?? ????????{?? ????????????jimmy=new?Jimmy();?? ????????}?? ????????public?void?Kiss()?? ????????{?? ????????????jimmy.Kiss();?? ????????}?? ????}?? ?? ????public?class?Jimmy?? ????{?? ????????public?void?Kiss()?? ????????{?? ????????????Console.WriteLine("kissing");?? ????????}?? ????}??
這樣導致Lily對Jimmy的依賴性非常強,緊耦合。
2.親友介紹:經(jīng)常Kiss同一個人令Lily有些厭惡了,她想嘗試新人,于是與Jimmy分手了,通過親朋好友(中間人)來介紹
public?class?Lily{?? ????????public?Boy?boy;??? ?? ????????public?Girl()?? ????????{?? ????????????boy=BoyFactory.createBoy();?? ????????}?? ????????public?void?Kiss()?? ????????{?? ????????????boy.Kiss();?? ????????}?? ????}??
親友介紹,固然是好。如果不滿意,盡管另外換一個好了。但是,親友BoyFactory經(jīng)常是以Singleton的形式出現(xiàn),不然就是,存在于Globals,無處不在,無處不能。實在是太繁瑣了一點,不夠靈活。我為什么一定要這個親友摻和進來呢?為什么一定要付給她介紹費呢?萬一最好的朋友愛上了我的男朋友呢?
?
3.父母包辦:一切交給父母,自己不用非吹灰之力,Lily在家只Kiss
public?class?Lily{?? ????????public?Boy?boy;??? ????????public?Girl(Boy?boy)?? ????????{?? ????????????this.boy=boy;?? ????????}?? ????????public?void?Kiss()?? ????????{?? ????????????this.boy.Kiss();?? ????????}?? ????}??
?
?
Well,這是對Girl最好的方法,只要想辦法賄賂了Girl的父母,并把Boy交給他。那么我們就可以輕松的和Girl來Kiss了。看來幾千年傳統(tǒng)的父母之命還真是有用哦。至少Boy和Girl不用自己瞎忙乎了。這就是IOC,將對象的創(chuàng)建和獲取提取到外部。由外部容器提供需要的組件。
在設(shè)計模式中我們應該還知道依賴倒轉(zhuǎn)原則,應是面向接口編程而不是面向功能實現(xiàn),好處是:多實現(xiàn)可以任意切換,我們的Boy應該是實現(xiàn)Kissable接口。這樣一旦Girl不想kiss可惡的Boy的話,還可以kiss可愛的kitten和慈祥的grandmother
好在.net中微軟有一個輕量級的IoC框架Unity,支持構(gòu)造器注入,屬性注入,方法注入如下圖所示
具體使用方法如下圖所示
using?System;?? ?? using?Microsoft.Practices.Unity;?? ?? ?? namespace?ConsoleApplication9?? {?? ????class?Program?? ????{?? ????????static?void?Main(string[]?args)?? ????????{?? ???????????? ????????????IUnityContainer?container=new?UnityContainer();?? ???????????? ????????????container.RegisterType<IKiss,?Boy>();?? ???????????? ????????????var?boy?=?container.Resolve<IKiss>();?? ????????????? ????????????Lily?lily?=?new?Lily(boy);?? ????????????lily.kiss();?? ????????}?? ????}?? ?? ?? ????public?interface?IKiss?? ????{?? ????????void?kiss();?? ????}?? ?????? ?? ????public?class?Lily:IKiss?? ????{?? ?? ????????public?IKiss?boy;??? ?? ????????public?Lily(IKiss?boy)?? ????????{?? ????????????this.boy=boy;?? ????????}?? ????????public?void?kiss()?? ????????{?? ????????????boy.kiss();?? ????????????Console.WriteLine("lily?kissing");?? ????????}?? ????}?? ?? ????public?class?Boy?:?IKiss?? ????{?? ????????public?void?kiss()?? ????????{?? ????????????Console.WriteLine("boy?kissing");?? ????????}?? ????}?? }??
如果采用配置文件注冊的話
<?xml?version="1.0"?encoding="utf-8"??>?? <configuration>?? ??<configSections>?? ????<section?name="unity"?type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/>?? ??</configSections>?? ??<unity>?? ????<containers>?? ??????<container?name="defaultContainer">?? ????????<register?type="命名空間.接口類型1,命名空間"?mapTo="命名空間.實現(xiàn)類型1,命名空間"?/>?? ????????<register?type="命名空間.接口類型2,命名空間"?mapTo="命名空間.實現(xiàn)類型2,命名空間"?/>?? ??????</container>?? ????</containers>?? ??</unity>?? </configuration>??
配置的后臺代碼:
UnityConfigurationSection?configuration?=?ConfigurationManager.GetSection(UnityConfigurationSection.SectionName)?? ????????????as?UnityConfigurationSection;?? configuration.Configure(container,?"defaultContainer");??
可以通過方法ResolveAll來得到所有注冊對象的實例: var?Instances =?container.Resolve<IKiss>(); Martin Fowler在那篇著名的文章《Inversion of Control Containers and the Dependency Injection pattern》中將具體依賴注入劃分為三種形式,即構(gòu)造器注入、屬性(設(shè)置)注入和接口注入,習慣將其劃分為一種(類型)匹配和三種注入:
類型匹配(Type Matching):雖然我們通過接口(或者抽象類)來進行服務調(diào)用,但是服務本身還是實現(xiàn)在某個具體的服務類型中,這就需要某個類型注冊機制來解決服務接口和服務類型之間的匹配關(guān)系; 構(gòu)造器注入(Constructor Injection):IoC容器會智能地選擇選擇和調(diào)用適合的構(gòu)造函數(shù)以創(chuàng)建依賴的對象。如果被選擇的構(gòu)造函數(shù)具有相應的參數(shù),IoC容器在調(diào)用構(gòu)造函數(shù)之前解析注冊的依賴關(guān)系并自行獲得相應參數(shù)對象; 屬性注入(Property Injection):如果需要使用到被依賴對象的某個屬性,在被依賴對象被創(chuàng)建之后,IoC容器會自動初始化該屬性; 方法注入(Method Injection):如果被依賴對象需要調(diào)用某個方法進行相應的初始化,在該對象創(chuàng)建之后,IoC容器會自動調(diào)用該方法。
?
?
我們創(chuàng)建一個控制臺程序,定義如下幾個接口(IA、IB、IC和ID)和它們各自的實現(xiàn)類(A、B、C、D)。在類型A中定義了3個屬性B、C和D,其類型分別為接口IB、IC和ID。其中屬性B在構(gòu)在函數(shù)中被初始化,意味著它會以構(gòu)造器注入的方式被初始化;屬性C上應用了DependencyAttribute特性,意味著這是一個需要以屬性注入方式被初始化的依賴屬性;屬性D則通過方法Initialize初始化,該方法上應用了特性InjectionMethodAttribute,意味著這是一個注入方法在A對象被IoC容器創(chuàng)建的時候會被自動調(diào)用。
public?interface?IA?{?}?? ????public?interface?IB?{?}?? ????public?interface?IC?{?}?? ????public?interface?ID?{?}?? ?? ????public?class?A?:?IA?? ????{?? ????????public?IB?B?{?get;?set;?}?? ????????[Dependency]?? ????????public?IC?C?{?get;?set;?}?? ????????public?ID?D?{?get;?set;?}?? ?? ????????public?A(IB?b)?? ????????{?? ????????????this.B?=?b;?? ????????}?? ????????[InjectionMethod]?? ????????public?void?Initalize(ID?d)?? ????????{?? ????????????this.D?=?d;?? ????????}?? ????}?? ????public?class?B?:?IB?{?}?? ????public?class?C?:?IC?{?}?? ????public?class?D?:?ID?{?}??
然后我們?yōu)樵搼锰砑右粋€配置文件,并定義如下一段關(guān)于Unity的配置。這段配置定義了一個名稱為defaultContainer的Unity容器,并在其中完成了上面定義的接口和對應實現(xiàn)類之間映射的類型匹配。
<?xml?version="1.0"?encoding="utf-8"??>?? <configuration>?? ??<configSections>?? ????<section?name="unity"?type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/>?? ??</configSections>?? ??<unity>?? ????<containers>?? ??????<container?name="defaultContainer">?? ????????<register?type="UnityDemo.IA,UnityDemo"?mapTo="UnityDemo.A,?UnityDemo"/>?? ????????<register?type="UnityDemo.IB,UnityDemo"?mapTo="UnityDemo.B,?UnityDemo"/>?? ????????<register?type="UnityDemo.IC,UnityDemo"?mapTo="UnityDemo.C,?UnityDemo"/>?? ????????<register?type="UnityDemo.ID,UnityDemo"?mapTo="UnityDemo.D,?UnityDemo"/>?? ??????</container>?? ????</containers>?? ??</unity>?? </configuration>??
最后在Main方法中創(chuàng)建一個代表IoC容器的UnityContainer對象,并加載配置信息對其進行初始化。然后調(diào)用它的泛型的Resolve方法創(chuàng)建一個實現(xiàn)了泛型接口IA的對象。最后將返回對象轉(zhuǎn)變成類型A,并檢驗其B、C和D屬性是否是空
class?Program?? ????{?? ????????static?void?Main(string[]?args)?? ????????{?? ????????????UnityContainer?container?=?new?UnityContainer();?? ????????????UnityConfigurationSection?configuration?=?ConfigurationManager.GetSection(UnityConfigurationSection.SectionName)?as?UnityConfigurationSection;?? ????????????configuration.Configure(container,?"defaultContainer");?? ????????????A?a?=?container.Resolve<IA>()?as?A;?? ????????????if?(null!=a)?? ????????????{?? ????????????????Console.WriteLine("a.B==null?{0}",a.B==null?"Yes":"No");?? ????????????????Console.WriteLine("a.C==null?{0}",?a.C?==?null???"Yes"?:?"No");?? ????????????????Console.WriteLine("a.D==null?{0}",?a.D?==?null???"Yes"?:?"No");?? ????????????}?? ????????}?? ????}??
從如下給出的執(zhí)行結(jié)果我們可以得到這樣的結(jié)論:通過Resolve<IA>方法返回的是一個類型為A的對象,該對象的三個屬性被進行了有效的初始化。這個簡單的程序分別體現(xiàn)了接口注入(通過相應的接口根據(jù)配置解析出相應的實現(xiàn)類型)、構(gòu)造器注入(屬性B)、屬性注入(屬性C)和方法注入(屬性D)
a.B == null ? No
a.C == null ? No
a.D == null ? No
總結(jié)
以上是生活随笔 為你收集整理的理解依赖注入(IOC) 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。