Unity應用架構設計(8)——使用ServiceLocator實現對象的注入

對象的 『注入』 是企業級軟件開發常常聽到的術語。若是你是一個 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();
        ...
    }
}複製代碼

遺憾的是,BarSomeService 又耦合了。而後你又改爲這樣:函數

public class Bar
{
    ISomeService _service;

    public Bar(ISomeService service) {
        _service = service;
    }

    public void DoSomething() {
        var foo = new Foo(_service);
        foo.DoSomething();
        ...
    }
}複製代碼

經過構造函數傳遞參數,BarSomeService 解耦了。但你打算怎樣去實例化 Bar 呢?測試

額...(-。-;)ui

Spring中的依賴注入

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來配置嗎?這會不會太麻煩了?

使用ServiceLocator實現對象的注入

其實對象的 『注入』 有不少實現方式,依賴注入 『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上,點擊此瞭解
歡迎關注個人公衆號:

相關文章
相關標籤/搜索