什么是Service Locator 模式?

服务定位模式(Service Locator Pattern)是一种软件开发中的设计模式,通过应用强大的抽象层,可对涉及尝试获取一个服务的过程进行封装。该模式使用一个称为"Service Locator"的中心注册表来处理请求并返回处理特定任务所需的必要信息。

场景描述

某类ClassA依赖于服务ServiceA和服务ServiceB,服务的具体类型需在编译时指定。

这种条件下有以下缺点:

  • 尝试替换或更新依赖项,必须更改类的源代码并且重新编译。
  • 依赖项的具体实现必须在编译时可用。
  • 测试该类非常困难,因为类对依赖项有直接的引用,则依赖项不能使用Stub或Mock对象替换。
  • 该类包含用于创建、定位和管理依赖项的重复代码。

设计目标

使用 Service Locator Pattern 来达成以下目标:

  • 把类与依赖项解耦,从而使这些依赖项可被替换或者更新。
  • 类在编译时并不知道依赖项的具体实现。
  • 类的隔离性和可测试性非常好。
  • 类无需负责依赖项的创建、定位和管理逻辑。
  • 通过将应用程序分解为松耦合的模块,达成模块间的无依赖开发、测试、版本控制和部署。

解决方案

创建一个 Service Locator,其包含各服务的引用,并且封装了定位服务的逻辑。在类中使用 Service Locator 来获取所需服务的实例。

Service Locator 模式并不描述如何实例化服务,其描述了一种注册和定位服务的方式。通常情况下,Service Locator 模式与工厂模式(Factory Pattern)和依赖注入模式(Dependency Injection Pattern)等结合使用。

服务定位器应该能够在不知道抽象类的具体类型的情况下定位到服务。例如,它可能会使用字符串或服务接口类型来影射服务,这允许在无需修改类的条件下替换依赖项的具体实现。

实现细节

通常 ServiceLocator 类提供 IServiceLocator 接口的实现单例,并负责管理该实例的创建和访问。ServiceLocator 类提供 IServiceLocator 接口的默认实现,例如 ActivatingServiceLocator 类,可以同时创建和定位服务。

注意事项

在使用 Service Locator 模式之前,请考虑以下几点:

  • 有很多程序中的元素需要管理。
  • 在使用之前必须编写额外的代码将服务的引用添加到服务定位器。
  • 类将对服务定位器有依赖关系。
  • 源代码变的更加复杂和难以理解。
  • 可以使用配置数据来定义运行时的关系。
  • 必须提供服务的实现。因为服务定位器模式将服务消费者与服务提供者解耦,它可能需要提供额外的逻辑。这种逻辑将保证在服务消费者尝试定位服务之前,服务提供者已被安装和注册。

相关模式

  • 依赖注入(Dependency Injection)。这种模式解决了与 Service Locator 模式相同的问题,但它使用不同的方法。
  • 控制反转(Inversion of Control)。Service Locator 模式是这种模式的特殊版本。它将应用程序的传统控制流程反转。它用被调用对象来代替控制过程的调用方。

参考信息

代码示例

Service Locator 的简单实现,使用静态类实现,未使用Singleton设计,仅作Mapping影射。

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Reflection;
  5.  
  6. namespace Infrastructure
  7. {
  8. /// <summary>
  9. /// 服务定位器
  10. /// </summary>
  11. public static class ServiceLocator
  12. {
  13. #region Fields
  14.  
  15. private static readonly Dictionary<Type, Type> _mapping
        = new Dictionary<Type, Type>();
  16. private static readonly Dictionary<Type, object> _resources
        = new Dictionary<Type, object>();
  17. private static object _operationLock = new object();
  18.  
  19. #endregion
  20.  
  21. #region Add
  22.  
  23. /// <summary>
  24. /// 添加注册资源
  25. /// </summary>
  26. /// <typeparam name="TClass">资源类型</typeparam>
  27. /// <param name="instance">资源实例</param>
  28. public static void Add<TClass>(object instance)
  29. where TClass : class
  30. {
  31. Add(typeof(TClass), instance);
  32. }
  33.  
  34. /// <summary>
  35. /// 添加注册资源
  36. /// </summary>
  37. /// <param name="typeOfInstance">资源类型</param>
  38. /// <param name="instance">资源实例</param>
  39. public static void Add(Type typeOfInstance, object instance)
  40. {
  41. if (typeOfInstance == null)
  42. throw new ArgumentNullException("typeOfInstance");
  43. if (instance == null)
  44. throw new ArgumentNullException("instance");
  45.  
  46. if (!(typeOfInstance.IsInstanceOfType(instance)))
  47. {
  48. throw new InvalidCastException(
  49. string.Format(CultureInfo.InvariantCulture,
  50. "Resource does not implement supplied interface: {0}",
           typeOfInstance.FullName));
  51. }
  52.  
  53. lock (_operationLock)
  54. {
  55. if (_resources.ContainsKey(typeOfInstance))
  56. {
  57. throw new ArgumentException(
  58. string.Format(CultureInfo.InvariantCulture,
            "Resource is already existing : {0}", typeOfInstance.FullName));
  59. }
  60. _resources[typeOfInstance] = instance;
  61. }
  62. }
  63.  
  64. #endregion
  65.  
  66. #region Get
  67.  
  68. /// <summary>
  69. /// 查找指定类型的资源实例
  70. /// </summary>
  71. /// <typeparam name="TClass">资源类型</typeparam>
  72. /// <returns>资源实例</returns>
  73. public static TClass Get<TClass>()
  74. where TClass : class
  75. {
  76. return Get(typeof(TClass)) as TClass;
  77. }
  78.  
  79. /// <summary>
  80. /// 查找指定类型的资源实例
  81. /// </summary>
  82. /// <param name="typeOfInstance">The type of instance.</param>
  83. /// <returns>资源实例</returns>
  84. public static object Get(Type typeOfInstance)
  85. {
  86. if (typeOfInstance == null)
  87. throw new ArgumentNullException("typeOfInstance");
  88.  
  89. object resource;
  90.  
  91. lock (_operationLock)
  92. {
  93. if (!_resources.TryGetValue(typeOfInstance, out resource))
  94. {
  95. throw new ResourceNotFoundException(typeOfInstance.FullName);
  96. }
  97. }
  98.  
  99. if (resource == null)
  100. {
  101. throw new ResourceNotInstantiatedException(typeOfInstance.FullName);
  102. }
  103.  
  104. return resource;
  105. }
  106.  
  107. /// <summary>
  108. /// 尝试查找指定类型的资源实例
  109. /// </summary>
  110. /// <typeparam name="TClass">资源类型</typeparam>
  111. /// <param name="resource">资源实例</param>
  112. /// <returns>是否存在指定资源类型的资源实例</returns>
  113. public static bool TryGet<TClass>(out TClass resource)
  114. where TClass : class
  115. {
  116. bool isFound = false;
  117.  
  118. resource = null;
  119. object target;
  120.  
  121. lock (_operationLock)
  122. {
  123. if (_resources.TryGetValue(typeof(TClass), out target))
  124. {
  125. resource = target as TClass;
  126. isFound = true;
  127. }
  128. }
  129.  
  130. return isFound;
  131. }
  132.  
  133. #endregion
  134.  
  135. #region Register
  136.  
  137. /// <summary>
  138. /// 注册类型
  139. /// </summary>
  140. /// <typeparam name="TClass">实体类型,类型限制为有公共无参构造函数</typeparam>
  141. public static void RegisterType<TClass>()
  142. where TClass : class, new()
  143. {
  144. lock (_operationLock)
  145. {
  146. _mapping[typeof(TClass)] = typeof(TClass);
  147. }
  148. }
  149.  
  150. /// <summary>
  151. /// 注册类型
  152. /// </summary>
  153. /// <typeparam name="TFrom">资源类型</typeparam>
  154. /// <typeparam name="TTo">实体类型,类型限制为有公共无参构造函数</typeparam>
  155. public static void RegisterType<TFrom, TTo>()
  156. where TFrom : class
  157. where TTo : TFrom, new()
  158. {
  159. lock (_operationLock)
  160. {
  161. _mapping[typeof(TFrom)] = typeof(TTo);
  162. _mapping[typeof(TTo)] = typeof(TTo);
  163. }
  164. }
  165.  
  166. /// <summary>
  167. /// 是否已注册此类型
  168. /// </summary>
  169. /// <typeparam name="TClass">资源类型</typeparam>
  170. /// <returns>是否已注册此类型</returns>
  171. public static bool IsRegistered<TClass>()
  172. {
  173. lock (_operationLock)
  174. {
  175. return _mapping.ContainsKey(typeof(TClass));
  176. }
  177. }
  178.  
  179. #endregion
  180.  
  181. #region Resolve
  182.  
  183. /// <summary>
  184. /// 获取类型实例
  185. /// </summary>
  186. /// <typeparam name="TClass">资源类型</typeparam>
  187. /// <returns>类型实例</returns>
  188. public static TClass Resolve<TClass>()
  189. where TClass : class
  190. {
  191. TClass resource = default(TClass);
  192.  
  193. bool existing = TryGet<TClass>(out resource);
  194. if (!existing)
  195. {
  196. ConstructorInfo constructor = null;
  197.  
  198. lock (_operationLock)
  199. {
  200. if (!_mapping.ContainsKey(typeof(TClass)))
  201. {
  202. throw new ResourceNotResolvedException(
  203. string.Format(CultureInfo.InvariantCulture,
            "Cannot find the target type : {0}", typeof(TClass).FullName));
  204. }
  205.  
  206. Type concrete = _mapping[typeof(TClass)];
  207. constructor = concrete.GetConstructor(
            BindingFlags.Instance | BindingFlags.Public, null, new Type[0], null);
  208. if (constructor == null)
  209. {
  210. throw new ResourceNotResolvedException(
  211. string.Format(CultureInfo.InvariantCulture,
            "Public constructor is missing for type : {0}", typeof(TClass).FullName));
  212. }
  213. }
  214.  
  215. Add<TClass>((TClass)constructor.Invoke(null));
  216. }
  217.  
  218. return Get<TClass>();
  219. }
  220.  
  221. #endregion
  222.  
  223. #region Remove
  224.  
  225. /// <summary>
  226. /// 移除指定类型的资源实例
  227. /// </summary>
  228. /// <typeparam name="TClass">资源类型</typeparam>
  229. public static void Remove<TClass>()
  230. {
  231. Teardown(typeof(TClass));
  232. }
  233.  
  234. /// <summary>
  235. /// 移除指定类型的资源实例
  236. /// </summary>
  237. /// <param name="typeOfInstance">资源类型</param>
  238. public static void Remove(Type typeOfInstance)
  239. {
  240. if (typeOfInstance == null)
  241. throw new ArgumentNullException("typeOfInstance");
  242.  
  243. lock (_operationLock)
  244. {
  245. _resources.Remove(typeOfInstance);
  246. }
  247. }
  248.  
  249. #endregion
  250.  
  251. #region Teardown
  252.  
  253. /// <summary>
  254. /// 拆除指定类型的资源实例及注册映射类型
  255. /// </summary>
  256. /// <typeparam name="TClass">资源类型</typeparam>
  257. public static void Teardown<TClass>()
  258. {
  259. Teardown(typeof(TClass));
  260. }
  261.  
  262. /// <summary>
  263. /// 拆除指定类型的资源实例及注册映射类型
  264. /// </summary>
  265. /// <param name="typeOfInstance">资源类型</param>
  266. public static void Teardown(Type typeOfInstance)
  267. {
  268. if (typeOfInstance == null)
  269. throw new ArgumentNullException("typeOfInstance");
  270.  
  271. lock (_operationLock)
  272. {
  273. _resources.Remove(typeOfInstance);
  274. _mapping.Remove(typeOfInstance);
  275. }
  276. }
  277.  
  278. #endregion
  279.  
  280. #region Clear
  281.  
  282. /// <summary>
  283. /// 移除所有资源
  284. /// </summary>
  285. public static void Clear()
  286. {
  287. lock (_operationLock)
  288. {
  289. _resources.Clear();
  290. _mapping.Clear();
  291. }
  292. }
  293.  
  294. #endregion
  295. }
  296. }

Service Locator 测试代码

  1. using System;
  2. using Infrastructure;
  3.  
  4. namespace ServiceLocatorTest
  5. {
  6. class Program
  7. {
  8. interface IServiceA
  9. {
  10. string GetData();
  11. }
  12.  
  13. class ServiceA : IServiceA
  14. {
  15. public string GetData()
  16. {
  17. return "This data is from ServiceA";
  18. }
  19. }
  20.  
  21. static void Main(string[] args)
  22. {
  23. ServiceLocator.RegisterType<IServiceA, ServiceA>();
  24. IServiceA serviceA = ServiceLocator.Resolve<IServiceA>();
  25. string data = serviceA.GetData();
  26. Console.WriteLine(data);
  27. Console.ReadKey();
  28. }
  29. }
  30. }

Service Locator 模式的更多相关文章

  1. Atitit。如何实现dip, di ,ioc ,Service Locator的区别于联系

    Atitit.如何实现dip, di ,ioc  ,Service Locator的区别于联系 1. Dip原则又来自于松耦合思想方向1 2. 要实现dip原则,有以下俩个模式1 3. Ioc和di的 ...

  2. 服务定位器(Service Locator)

    服务定位器(Service Locator) 跟DI容器类似,引入Service Locator目的也在于解耦.有许多成熟的设计模式也可用于解耦,但在Web应用上, Service Locator绝对 ...

  3. .NET 服务器定位模式(Service Locator Pattern)——Common Service Locator

    本文内容 场景 目标 解决方案 实现细节 思考 相关模式 更多信息 参考资料 Common Service Locator 代码很简单,它一般不会单独使用,而是作为一个单件模式,与像 .net Uni ...

  4. 【IOC--Common Service Locator】不依赖于某个具体的IoC

    你在你的应用程序应用IoC容器了吗,你是否希望不依赖于某个具体的IoC,微软的模式与实践团队在Codeplex上发布的Common Service Locator.Common Service Loc ...

  5. [Design Pattern] Service Locator Pattern 简单案例

    Service Locator Pattern,即服务定位模式,用于定位不同的服务.考虑到 InitialContext::lookup 的成本比较高,提供了 Cache 类缓存以定位到的服务. 代码 ...

  6. 依赖注入与Service Locator

    为什么需要依赖注入? ServiceUser是组件,在编写者之外的环境内被使用,且使用者不能改变其源代码. ServiceProvider是服务,其类似于ServiceUser,都要被其他应用使用,不 ...

  7. PHP中应用Service Locator服务定位及单例模式

    单例模式将一个对象实例化后,放在静态变量中,供程序调用. 服务定位(ServiceLocator)就是对象工场Factory,调用者对象直接调用Service Locator,与被调用对象减轻了依赖关 ...

  8. Microsoft实现的IOC DI之 Unity 、Service Locator、MEF

    这几个工具的站点 Microsoft Unity  http://unity.codeplex.com Service Locator http://commonservicelocator.code ...

  9. 【转】Understanding Inversion of Control, Dependency Injection and Service Locator Print

    原文:https://www.dotnettricks.com/learn/dependencyinjection/understanding-inversion-of-control-depende ...

随机推荐

  1. SqlSugar常用增删改操作

    一.添加数据 特别说明: 1.特别说明:对于自增长列的表插入数据后,当前自增长列的字段,仍旧为0,但可以通过Insert方法的返回值来获取 SqlSugarClient db = SugarConte ...

  2. Scoket简介

    我们很多人都听说过Socket编程也称网络编程,在我们当今的社会中网络已经深入到我们的生活中了,计算机的网络通信也成为我们生活中必不可少的一部分.而实现我们网络通信就得依靠网络编程,让我们的计算机之间 ...

  3. java - String 浅谈

    /** * String s1 = "a"; * 编译器会先检查常量池中是否已经有"a": * 如果没有,则在常量池先创建,后引用. * 如果有,则直接引用; ...

  4. AngularJS code converage

    karma-coverage The easiest way is to keep karma-coverage as a devDependency in your package.json. Mo ...

  5. 关于NSNull和nil

    在做接口数据的json解析字典的时候,一般会进行非空判断,比如一个字符串: if(str!=nil){ //do something double data=[str doubleValue]; } ...

  6. 1 Yoga3 系统装机总结.

    1- Yoga 3 存在串口驱动不安装, 那么触摸屏不能用的情况, 打破了以往对触摸屏-"纯外设" 的设想, 与系统有关!!! 2- 系统安装总结: 1) BIOS中设置UEFI ...

  7. Top 12 Best Free Network Monitoring Tools (12种免费网络监控工具)

    1) Fiddler Fiddler(几乎)是适用于任何平台和任何操作系统的最好的免费网络工具,并提供了一些广受欢迎的关键特性.如:性能测试.捕捉记录HTTP/HTTPs请求响应.进行web调试等很多 ...

  8. 使用ArrayList对大小写字母的随机打印

    从a~z以及A~Z随机生成一个字母并打印:打印全部的字母 package com.liaojianya.chapter1; import java.util.ArrayList; /** * This ...

  9. C# 绘制窗体客户非客户区要用WM_PAINT和WM_NCPAINT

    窗体分为两部分:客户区(Client area)和非客户区(Non-Client area) WM_PAINT消息.OnPaint()方法.GetDC()API函数都是处理窗体客户区绘制的   而标题 ...

  10. MySQL设置

    在MySQL的使用中很容易出现乱码的情况. 实际上在MySQL中有个地方表明了系统中所用到的所有的字符集. 例如: 从中可以看出,对于server和database的默认字符集都是latin1,这样很 ...