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上,点击此了解
欢迎关注个人公众号:

相关文章
相关标签/搜索