控制反轉IOC, 全稱 “Inversion of Control”。依賴註入DI, 全稱 “Dependency Injection”。 面向的問題:軟體開發中,為了降低模塊間、類間的耦合度,提倡基於介面的開發,那麼在實現中必須面臨最終是有“誰”提供實體類的問題。(將各層的對象以松耦合的方式組織起 ...
控制反轉IOC, 全稱 “Inversion of Control”。依賴註入DI, 全稱 “Dependency Injection”。
面向的問題:軟體開發中,為了降低模塊間、類間的耦合度,提倡基於介面的開發,那麼在實現中必須面臨最終是有“誰”提供實體類的問題。(將各層的對象以松耦合的方式組織起來,各層對象的調用面向介面。)
當一個類的實例需要另一個類的實例協助時,在傳統的程式設計過程中,通常有調用者來創建被調用者的實例。
然後,採用依賴註入原則,創建被調用者的實例的工作不再由調用者完成,而是由IOC容器來完成,這就是“控制反轉”的意思,然後,將其註入調用者,因此也稱為 “依賴註入”。
Martin Fowler,在其著名的文章《Inversion of Control Containers and the Dependency Injection pattern》中將具體依賴註入劃分為三種形式,即構造器註入、屬性(設置)註入和介面註入。
習慣將其劃分為一種(類型)匹配和三種註入:
- 類型匹配(Type Matching):雖然我們通過介面(或者抽象類)來進行服務調用,但是服務本身還是實現在某個具體的服務類型中,這就需要某個類型註冊機制來解決服務介面和服務類型之間的匹配關係;
- 構造器註入(Constructor Injection):IoC容器會智能地選擇選擇和調用適合的構造函數以創建依賴的對象。如果被選擇的構造函數具有相應的參數,IoC容器在調用構造函數之前解析註冊的依賴關係並自行獲得相應參數對象;
- 屬性註入(Property Injection):如果需要使用到被依賴對象的某個屬性,在被依賴對象被創建之後,IoC容器會自動初始化該屬性;
- 方法註入(Method Injection):如果被依賴對象需要調用某個方法進行相應的初始化,在該對象創建之後,IoC容器會自動調用該方法
創建一個控制台程式,定義如下幾個介面(IA、IB、IC和ID)和它們的實現類(A、B、C、D)。在類型A中定義了三個屬性B、C和D,其參數類型分別為IB、IC和ID。
其中,
屬性B作為構函數的參數,認為它會以構造器註入的方式被初始化 (??);
屬性C應用了DependencyAttribute特性,意味著這是一個需要以屬性註入方式被初始化的依賴屬性;
屬性D則通過方法Initialize初始化,該方法上應用了特性InjectionMethodAttribute, 意味著這是一個方法註入,在A對象被Ioc容器創建的時候,D會被自動調用。
Microsoft有一個輕量級的IoC框架Unity, 支持構造器註入,屬性註入,方法註入。對於C#語言,由於語法元素上本身較其他語言豐富許多,如何實施註入還有些技巧和特色之處。
下麵介紹如下:
測試類:
namespace UnityDemo { 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 Initialize(ID d) { this.D = d; } } public class B : IB { } public class C : IC { } public class D : ID { } }
配置註冊:
<?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> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> </configuration>
Main方法中,創建一個Ioc容器的UnityContainer對象,並載入配置信息對其初始化,然後調用它的泛型的Resolve方法創建一個實現了泛型介面IA的對象。
最後將返回對象轉換成類型A, 並逐一檢驗B,C和D屬性是否為空,即初始化情況。
namespace UnityDemo { class Program { static void Main(string[] args) { var container = new UnityContainer(); var 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"); } } } }
執行結果:
分別體現了介面註入、構造器註入(屬性B)、屬性註入(屬性C)和方法註入(屬性D)。
JACK D. @ NJ USA