對象的 『注入』 是企業級軟件開發常常聽到的術語。若是你是一個 Java 程序員,必定對注入有着深入的映像。無論是SSH框架仍是SSM框架,Spring 全家桶永遠是繞不過去的彎。經過依賴注入,能夠有效的解耦應用程序。在uMVVM框架中,我提供了另一種對象注入的方式,稱爲Service Locator 『服務定位模式』 。與Spring的依賴注入不一樣的是,Service Locator 內部以字典的形式維護了對象的依賴關係,外部經過Key的形式獲取 『Resolve』 到對應的Value,從而達到解耦。html
簡而言之,爲了解耦,達到 不去依賴 具體的對象。git
實際上解耦是個很是 『虛』 的概念,只有軟件到達必定的複雜度以後纔會明白解耦和的好處,對於一個簡單如『Hello World』程序而言,你很難理解爲何須要解耦。程序員
假設有個 Foo 類,須要經過調用 SomeService 對象的方法去執行一些任務。很簡單的需求,你可能會這樣寫:github
public class Foo
{
ISomeService _service;
public Foo() {
_service = new SomeService();
}
public void DoSomething() {
_service.PerformTask();
...
}
}複製代碼
這固然沒問題,但有隱患,Foo 緊耦合了 SomeService,當需求變了,你不得不打開 Foo 類,而後找到構造函數,從新調用另外的 Service,改完以後編譯,而後部署、測試等等。若是是Web程序,你還得在等到晚上去部署。spring
既然緊耦合了,那就解耦,你可能會這樣寫:設計模式
public class Foo
{
ISomeService _service;
public Foo(ISomeService service) {
_service = service;
}
public void DoSomething() {
_service.PerformTask();
...
}
}複製代碼
這樣很不錯,Foo 與具體的 Service 解耦了,那怎樣去實例化 Foo 呢?好比有一個 Bar 類:框架
public class Bar
{
public void DoSomething() {
var foo = new Foo(new SomeService());
foo.DoSomething();
...
}
}複製代碼
遺憾的是,Bar 和 SomeService 又耦合了。而後你又改爲這樣:函數
public class Bar
{
ISomeService _service;
public Bar(ISomeService service) {
_service = service;
}
public void DoSomething() {
var foo = new Foo(_service);
foo.DoSomething();
...
}
}複製代碼
經過構造函數傳遞參數,Bar 和 SomeService 解耦了。但你打算怎樣去實例化 Bar 呢?測試
額...(-。-;)ui
Spring中將上述 Foo、Bar 類對SomeService的依賴關係,經過構造函數或者setter方法來實現對象的注入。
<!-- 建立對象:Bar-->
<bean id="barId" class="com.xxx.Bar" >
<property name="service" ref="someServiceId"></property>
</bean>
<!-- 建立SomeService實例 -->
<bean id="someServiceId" class="com.xxx.SomeService"></bean>複製代碼
能夠看到Spring將依賴關係配置到XML中,在運行時,從IoC容器工廠獲取 『Bean(即:實例)』 並將依賴關係注入。
難道咱們須要在Unity3D 中定義XML來配置嗎?這會不會太麻煩了?
其實對象的 『注入』 有不少實現方式,依賴注入 『DI』 只是其中一種,大名鼎鼎的Spring框架就是很是優秀的依賴注入框架,而uMVVM中實現的注入式經過ServiceLocator實現。
什麼是ServiceLocator?
簡單說ServiceLocator內部以字典的形式維護了對象的依賴關係,外部經過Key的形式獲取到對應的Value,從而達到解耦,以下圖所示:
要實現對象的 『注入』 ,還缺一個很是重要的對象,就是IoC容器工廠,全部須要被注入的對象都是由容器工廠建立。那咱們哪裏去找工廠呢?還記得上篇文章的內容了嗎?咱們已經預先定義了3種不一樣建立對象的工廠,他們分別爲 Singleton Factory,Transient Factory以及 Pool Factory,這些就是咱們須要的IoC工廠。
既然 ServiceLocator內部以字典的形式維護了依賴關係,那麼首先須要建立一個字典:
private static readonly Dictionary<Type, Func<object>> Container = new Dictionary<Type, Func<object>>();複製代碼
注意到字典的Value了嗎,這是一個 Fun ,本質上是一段匿名函數,只有當真正須要的時候,執行這段匿名函數,返回對象。這是一個很是好的設計,也是懶加載的核心。Swift 和 C# 4.0 的Lazy 核心和代碼就是匿名函數。
咱們再對Service Locator進行加強,既然要經過字典來維護依賴關係,那咱們必須往字典裏註冊它們,結合咱們的工廠,經過ServiceLocator獲取的對象能夠是單例Singleton對象或者臨時Transient對象:
/// <summary>
/// 對每一次請求,只返回惟一的實例
/// </summary>
/// <typeparam name="TInterface"></typeparam>
/// <typeparam name="TInstance"></typeparam>
public static void RegisterSingleton<TInterface, TInstance>() where TInstance : class, new() {
Container.Add(typeof(TInterface), Lazy<TInstance>(FactoryType.Singleton));
}
/// <summary>
/// 對每一次請求,只返回惟一的實例
/// </summary>
/// <typeparam name="TInstance"></typeparam>
public static void RegisterSingleton<TInstance>() where TInstance : class, new() {
Container.Add(typeof(TInstance), Lazy<TInstance>(FactoryType.Singleton));
}
/// <summary>
/// 對每一次請求,返回不一樣的實例
/// </summary>
/// <typeparam name="TInterface"></typeparam>
/// <typeparam name="TInstance"></typeparam>
public static void RegisterTransient<TInterface, TInstance>() where TInstance : class, new() {
Container.Add(typeof(TInterface),Lazy<TInstance>(FactoryType.Transient));
}
/// <summary>
/// 對每一次請求,返回不一樣的實例
/// </summary>
/// <typeparam name="TInstance"></typeparam>
public static void RegisterTransient<TInstance>() where TInstance : class, new() {
Container.Add(typeof(TInstance),Lazy<TInstance>(FactoryType.Transient));
}
private static Func<object> Lazy<TInstance>(FactoryType factoryType) where TInstance : class, new() {
return () =>
{
switch (factoryType)
{
case FactoryType.Singleton:
return _singletonObjectFactory.AcquireObject<TInstance>();
default:
return _transientObjectFactory.AcquireObject<TInstance>();
}
};
}複製代碼
能夠看到,其實本質上真的很簡單,就是一個 Key:Value 形式的字典,最後提供一個Resolve方法,能夠經過Key來獲取對應的對象:
/// <summary>
/// 從容器中獲取一個實例
/// </summary>
/// <returns></returns>
private static object Resolve(Type type) {
if (!Container.ContainsKey(type))
{
return null;
}
return Container[type]();
}複製代碼
使用起來也很是方便,在一個全局初始化文件中去定義這些依賴關係:
ServiceLocator.RegisterSingleton<IUnitRepository,UnitRepository>();複製代碼
而後在你的任何業務代碼中以Resolve的形式獲取對象:
ServiceLocator.Resolve<IUnitRepository>();複製代碼
使用構造函數或者setter方法依賴注入也好,仍是使用ServiceLocator也罷,它們本質上都是去解耦。對象的注入通常須要結合IoC容器,咱們已經定義了3種不一樣的IoC工廠容器。詳細能夠翻閱前一篇文章:『Unity應用架構設計(7)——IoC工廠理念先行』。這兩篇文章對於初學者來講是有難度的,由於不少概念都是Web開發常常遇到的,若是須要額外的資料,建議翻閱 《設計模式》 相關書籍。
源代碼託管在Github上,點擊此瞭解
歡迎關注個人公衆號: