IOC的基本概念是:不創建對象,但是描述創建它們的方式。在代碼中不直接與對象和服務連接,但在配置文件中描述哪一個組件需要哪一項服務。容器負責將這些聯系在一起。
舉個例子,組件A中有類ClassA,組件B中有接口IB和其對應的實現類B1和B2。
那么,現在ClassA需要利用IB接口來做一些事情,例如:
public class ClassA {
public void DoSomething() {
IB b = ???
b.DoWork();
}
}
現在的問題來了,IB b = ??? 中這三個???要寫什么代碼?是要寫成 IB b = new B1(),還是要寫成IB b = new B2() ?
不管是哪一種,都會讓ClassA強依賴於IB的實現。
在上面這種方案中,ClassA通過new一個B1或B2來實現對IB的依賴的獲取,換句話說,ClassA在主動獲取依賴。
這樣的設計會讓ClassA很難擴展,那我們要改良設計:使用依賴注入。上面說到了,問題出在new這里,也就是依賴是Class去主動獲取的,那我們就要解決這個問題:不要去主動獲取對IB的依賴(通過new),而讓這個依賴從ClassA的外面“注入”進來。注入有多種方式,比較常用的一種是通過構造函數注入,那么,我們要把ClassA改成:
public class ClassA {
private IB b;
public ClassA(IB b) {
this.b = b;
}
public DoSomething() {
this.b.DoWork();
}
}
可以看到,通過把IB這個依賴從構造函數中“注”進來后,ClassA就不依賴IB的實現了。還可以發現,這個重構過程中,我們是把"ClassA主動獲取對IB的依賴”變成“把對IB的依賴從外部注入到ClassA中”,依賴的方向反轉了,所以,依賴注入又稱“控制反轉”。
IoC框架(如Unity, Autofac,Spring.Net),其中Unity是微軟自己封裝的,另外可以利用Extnesions.Dependency動態生成類(參考之前的將RFCTable轉為List<T>利用依賴注入動態生成類的例子),參考之前的代碼
下面以Unity作為介紹:
Unity是一個輕量級的可擴展的依賴注入容器,支持構造函數,屬性和方法調用注入。Unity可以處理那些從事基於組件的軟件工程的開發人員所面對的問題。構建一個成功應用程序的關鍵是實現非常松散的耦合設計。下面介紹一下c#中使用unity的方法(我是以webapi項目為例,但本例中並沒有針對webapi做特殊處理)
新建一個mvc4 webapi項目,下面的例子只用到get方法
用nuget安裝unity,如圖
新建一個接口類,以及繼承該接口的兩個類
直接在api/values的get中嘗試簡單實現unity
using (IUnityContainer container = new UnityContainer())
{
container.RegisterType<IBook, BBook>();
IBook a = container.Resolve<IBook>();
var strResult = a.Write();
return strResult;
}
然后在瀏覽器中查看,頁面顯示的返回值,是BBook的
如果container.RegisterType<IBook, BBook>();中的BBook改為ABook,返回值就是ABook的內容
config中代碼如下
在configSections中加入
<section name="unity"
type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
在configuration中加入
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<container>
<register type="testunity.Models.IBook,testunity" mapTo="testunity.Models.ABook, testunity" />
</container>
</unity>
注意 type="testunity.Models.IBook,testunity" mapTo="testunity.Models.ABook, testunity"
testunity.Models.IBook是命名空間加類名
testunity是程序集的名稱
然后cs的代碼改成
using (IUnityContainer container = new UnityContainer())
{
UnityConfigurationSection configuration = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
configuration.Configure(container);
IBook a = container.Resolve<IBook>();
var strResult = a.Write();
return strResult;
}
在瀏覽器中可以看到返回結果對應的是config中register的那個類
每次調用都要寫IUnityContainer container = new UnityContainer()顯然不是好辦法
那就把container封裝到一個單列類中
簡單實現如下
public class ServiceLocator:IServiceProvider
{
private readonly IUnityContainer _container;
private static readonly ServiceLocator instance = new ServiceLocator();
private ServiceLocator()
{
UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
_container = new UnityContainer();
section.Configure(_container);
}
public static ServiceLocator Instance
{
get { return instance; }
}
public object GetService(Type serviceType)
{
return _container.Resolve(serviceType);
}
public T GetService<T>()
{
return _container.Resolve<T>();
}
}
cs代碼修改如下
IBook a = ServiceLocator.Instance.GetService<IBook>();
var strResult = a.Write();
return strResult;