曾幾何時,從Spring中認識了IoC這個詞語,但它並沒有撩動我的心,原因很簡單,我懵懂年代的程序連個靜態Factory來創建實例都顯得有點多餘,Ioc更沒有登場的必要。真正認識IoC也許是在使用Castle的時候,記得印象最深刻的一個情景是,我們團隊當時的BS項目,開始設計時使用了一個第三方組件,後來需求變動了,這個第三方組件的功能卻跟不上需求,我們不得不要換用一個新的組件,但是項目已經部署,客戶要求更新後如果出問題能自行恢復到舊版本,當時想到一個笨拙的方法是,要求客戶備份一下程序,然後出問題覆蓋之就可以了,但是這樣有個個問題,新開發模塊和維護模塊是同時進行部署的,怎能保證同一個模塊中一部分使用新的程序,而一部分要恢復到舊程序中去呢,顯然在舊辦法中不重新編譯就達不到這個要求,而其中即使有良好的版本控制,也是一兩個文件Undo就能了事的,幸好這時Windsor跳進了我們的視線,使用它通過配置文件實現組件的自由設換,幫助我們輕鬆度過難關。這雖然不是一個應用IoC的正途例子,卻在實際中幫了我們大忙^_^,要想正統的瞭解IoC,就要重溫Martin Fowler大叔的經典文章《Inversion of Control Containers and the Dependency Injection pattern》中文版
隨着P&P團隊發佈Entlib 4.0 的藍圖,Unity慢慢映入我的眼簾,又是一個輕量級的IoC,現在終於發佈了1.0版本,到底性能如何,讓我們先看看附帶的StopLight QuickStart
1.在代碼中註冊對象的映射
代碼如下:
.RegisterType<ILogger, TraceLogger>()
.RegisterType<IStoplightTimer, RealTimeTimer>();
// 爲容器註冊默認注入實例
Application.Run(container.Resolve<StoplightForm>());
基本的流程是:創建注入容器 => 註冊對象映射 => 調用對象時根據映射關係創建對象
在這個QuickStart中創建對像具體的流程是
StoplightForm -》StoplightPresenter -》Stoplight -》ILogger -》TraceLogger
-》StoplightSchedule -》IStoplightTimer -》RealTimeTimer -》ILogger(有重複的話,會檢查是否已經曾經實例化過,如果有,找出所使用實例化的策略去實例化或者不重新實例化(單件模式))
在要實例化接口的地方使用Dependency屬性,如(這裏使用屬性來做關聯處理,對調試造成一定的麻煩,使用Unity時可能會在這裏跟蹤代碼丟失的問題,唯一解決的辦法就是直接使用Unity的源碼嵌套到項目中進行使用 ^_^)
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
具體在Unity裏面的是實現是
{
Guard.ArgumentNotNull(context, "context");
NamedTypeBuildKey key = new NamedTypeBuildKey(type, name);
IBuilderContext recursiveContext = context.CloneForNewBuild(key, null);
// 根據註冊關聯的信息按照響應的實例策略一層一層實例化
return recursiveContext.Strategies.ExecuteBuildUp(recursiveContext);
}
同一接口也可以註冊多個實例
如添加一個實現類
{
ILogger Members
}
註冊代碼改爲
.RegisterType<ILogger, TraceLogger>()
.RegisterType<ILogger, MakeFunLogger>()
.RegisterType<IStoplightTimer, RealTimeTimer>();
大家也許和我有同樣的疑問,這裏同時爲ILogger註冊了兩個實例,那麼運行時到底是實例哪一個呢?答案是後註冊的那個(這個印象中和windsor好像是相反的,呵呵)
2.在配置文件中註冊對象的映射
1)添加Configuration相關應用
Microsoft.Practices.Unity.Configuration;
System.Configuration
2)創建建App.Config
<configuration>
<configSections>
<!--
使用編譯好的Unity組件
<section name="unity"
type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
Microsoft.Practices.Unity.Configuration, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
-->
<!--
直接使用Unity源碼
(ps:我習慣應用組件不表明Version,因爲組件版本是在更新的,如果限死Version有時候很麻煩,當然了,原則上同一組件的接口是不變纔行,^_^)
-->
<section name="unity"
type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
Microsoft.Practices.Unity.Configuration" />
</configSections>
<unity>
<typeAliases>
<!--
創建對象別名,不是必須的
其type格式是:對像的全名,程序集名
-->
<typeAlias alias="ILogger"
type="StopLight.ServiceInterfaces.ILogger, StopLight" />
<typeAlias alias="IStoplightTimer"
type="StopLight.ServiceInterfaces.IStoplightTimer, StopLight" />
<typeAlias alias="TraceLogger"
type="StopLight.ServiceImplementations.TraceLogger, StopLight" />
<typeAlias alias="RealTimeTimer"
type="StopLight.ServiceImplementations.RealTimeTimer, StopLight" />
</typeAliases>
<containers>
<container name="containerOne">
<types>
<!--
註冊映射,這裏使用默認的生命週期和實例策略,具體設置可以看文檔 ^_^
-->
<type type="ILogger" mapTo="TraceLogger"/>
<type type="IStoplightTimer" mapTo="RealTimeTimer" />
</types>
</container>
</containers>
</unity>
</configuration>
3)修改註冊代碼
IUnityContainer container = new UnityContainer();
UnityConfigurationSection section
= (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Containers["containerOne"].Configure(container);
這樣就可以使用文件進行配置 ^_^