Common Service Locator

CommonServiceLocator(CSL)是由Microsoft patterns & practices团队所设计的一个组件,它是 Unity 的一部分。CommonServiceLocator的代码很简单,它主要定义了一些接口和抽象方法,一般与 Unity 这样的依赖注入框架(Dependency Injection Framework)一起使用。

CommonServiceLocator本质上是对一个IoC 容器进行封装,它使用一个全局的、静态的内存对象(a static in-memory object)来保存对IoC容器实例的引用。它把应用程序对于某一个IoC容器的依赖转换成了对CommonServiceLocator的依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public static class ServiceLocator
{
private static ServiceLocatorProvider _currentProvider;

/// <summary>
/// The current ambient container.
/// </summary>
public static IServiceLocator Current
{
get
{
if (!IsLocationProviderSet)
throw new InvalidOperationException(Constants.ServiceLocationProviderNotSetMessage);

return _currentProvider();
}
}

/// <summary>
/// Set the delegate that is used to retrieve the current container.
/// </summary>
/// <param name="newProvider">Delegate that, when called, will return
/// the current ambient container.</param>
public static void SetLocatorProvider(ServiceLocatorProvider newProvider)
{
_currentProvider = newProvider;
}

public static bool IsLocationProviderSet => _currentProvider != null;
}

以下主流的IoC容器针对CommonServiceLocator都有相应的适配器:

如果你想使用的IoC容器没有提供对CommonServiceLocator的适配器,那我们可以自己实现一个,只需要继承CommonServiceLocator中的ServiceLocatorImplBase抽象类,实现两个抽象方法即可。
common-service-locator-class-diagram

具体的代码可以参考Autofac提供的实现

使用CommonServiceLocator

在使用CommonServiceLocator之前,需要先注册Provider,下面我们以Autofac为例说明。

1
2
3
4
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule(new MainModule());
var container = containerBuilder.Build();
ServiceLocator.SetLocatorProvider(() => new AutofacServiceLocator(container));

Provider注册完成后,我们就可以在任意地方使用CommonServiceLocator来获取所需要的服务实例了。最常用的方法就是支持泛型的GetInstance方法了。

1
var typeMapper = ServiceLocator.Current.GetInstance<ITypeMapper>();

我在这里强调可以在任意地方使用CommonServiceLocator来获取所需要的服务实例,是CommonServiceLocator相对于单独使用IoC容器的一个优势。对于无法使用构造器注入或者属性注入的一些上下文,CommonServiceLocator的价值就体现出来了。

Service Locator Pattern

简单聊完了CommonServiceLocator后,我们再来回顾下服务定位器模式(Service Locator Pattern)。

服务定位器模式是软件开发中的一种创建型设计模式,通过复杂的抽象层,封装获得服务所涉及的过程。该模式使用一个称为”Service Locator”的中心注册表来处理请求并返回处理特定任务所需的服务。这个中心注册表可以是一个简单的字典结构,也可以是一个复杂的IoC容器。
service-locator-pattern

服务定位器模式并不描述如何实例化服务,其描述了一种注册和定位服务的方式。服务定位器应该能够在不知道抽象类的具体类型的情况下定位到服务。通常情况下,服务定位器模式与工厂模式(Factory Pattern)或依赖注入模式(Dependency Injection Pattern)等结合使用。

Dependency Inversion Principle、Inversion of Control与Dependency Injection

依赖反转、控制反转、依赖注入等词汇是不是傻傻搞不清楚?如果你和我一样,那么请往下读。

Dependency Inversion Principle

首先,我们需要先了解程序设计领域中一个非常大的概念 —— 面向对象设计的五个基本原则:

  1. 单一功能原则(Single responsibility principle)
  2. 开闭原则(Open–closed principle)
  3. 里氏替换原则(Liskov substitution principle)
  4. 接口隔离原则(Interface segregation principle)
  5. 依赖反转原则(Dependency inversion principle)

这五大原则,根据其首字母我们称之为SOLID。因此,我们知道依赖反转原则(Dependency inversion principle,DIP)就是SOLID中的D。它是指一种特定的解耦形式,使得高层次的模块不依赖于低层次的模块的实现细节,依赖关系被颠倒(反转),从而使得低层次模块依赖于高层次模块的抽象需求。DIP的目的是解耦,具体有两个原则:

  • 高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象。
  • 抽象不应该依赖于具体实现,而具体实现应该依赖于抽象。

dependency-inversion-principle 左边的图中,高层对象A依赖于底层对象B的实现;右边的图中,把高层对象A对底层对象的需求抽象为一个接口A,底层对象B实现了接口A,这就是依赖反转。

Inversion of Control与Dependency Injection

控制反转(Inversion of Control,IoC)是一个设计模式,它提倡我们反转面向对象设计中的各种控制,以达到各个类之间的松耦合,它提供了实现依赖反转原则的一些具体方案。这里“控制”的含义是除了一个类本职之外的其它所有工作,如整个软件流程的控制,依赖的创建和绑定等。控制反转是把不属于类的职责抽离出来,以实现单一功能原则(Single responsibility principle),让一个专门的“第三方”来做处理这些事。所以它的外延其实是很广的,我们常说的IoC容器(IoC Container)只是一个专门用来处理依赖创建的“第三方”,它是一个软件框架。在.NET平台上,这样的IoC容器有Unity, Autofac,StructureMap等。

控制反转现在更强调的是反转依赖的创建和绑定。对于软件流程的反转,控制反转更多的是对现代GUI程序的一种基本事实描述。以往基于命令行的程序和用户的交互是一个顺序的流程,而现代GUI程序则是基于事件机制,根据用户的操作来触发不同的代码,即用户控制了软件的流程,与命令行程序相比则是实现了控制的反转。

实现控制反转主要有两种方式:依赖注入(Dependency Injection)和依赖查找(Dependency Lookup)。两者的区别在于,前者是被动的接收对象;而后者是主动索取相应类型的对象,并且获得依赖对象的时间也可以在代码中自由控制。服务定位器模式(Service Locator Pattern)可以看成是依赖查找的一种更具体的实现方式。

它们之间的关系可以用下面这张图表示。
dip-ioc-di

对Service Locator Pattern的批评

Service Locator Pattern被认为是一种Anti-Pattern,饱受批评。这主要以Mark Seemann的文章——Service Locator is an Anti-Pattern为代表。文章指出,Service Locator Pattern对使用者隐藏了自身的依赖,容易导致运行时错误,并且难以测试和维护。我同意这位大师的看法,但我认为Service Locator Pattern在使用依赖注入无法满足的一些场景中,还是有其存在的价值。所谓模式,就是人们解决某一类问题的方法论,将其总结归纳到理论高度,那就是模式。没有什么反模式,只有好的模式以及不那么好的模式。

相关链接