Service Locator 模式

什麼是Service Locator 模式?

服務定位模式(Service Locator Pattern)是一種軟件開發中的設計模式,經過應用強大的抽象層,可對涉及嘗試獲取一個服務的過程進行封裝。該模式使用一個稱爲"Service Locator"的中心註冊表來處理請求並返回處理特定任務所需的必要信息。html

場景描述

某類ClassA依賴於服務ServiceA和服務ServiceB,服務的具體類型需在編譯時指定。web

這種條件下有如下缺點:設計模式

  • 嘗試替換或更新依賴項,必須更改類的源代碼而且從新編譯。
  • 依賴項的具體實現必須在編譯時可用。
  • 測試該類很是困難,由於類對依賴項有直接的引用,則依賴項不能使用Stub或Mock對象替換。
  • 該類包含用於建立、定位和管理依賴項的重複代碼。

設計目標

使用 Service Locator Pattern 來達成如下目標:app

  • 把類與依賴項解耦,從而使這些依賴項可被替換或者更新。
  • 類在編譯時並不知道依賴項的具體實現。
  • 類的隔離性和可測試性很是好。
  • 類無需負責依賴項的建立、定位和管理邏輯。
  • 經過將應用程序分解爲鬆耦合的模塊,達成模塊間的無依賴開發、測試、版本控制和部署。

解決方案

建立一個 Service Locator,其包含各服務的引用,而且封裝了定位服務的邏輯。在類中使用 Service Locator 來獲取所需服務的實例。函數

Service Locator 模式並不描述如何實例化服務,其描述了一種註冊和定位服務的方式。一般狀況下,Service Locator 模式與工廠模式(Factory Pattern)和依賴注入模式(Dependency Injection Pattern)等結合使用。測試

服務定位器應該可以在不知道抽象類的具體類型的狀況下定位到服務。例如,它可能會使用字符串或服務接口類型來影射服務,這容許在無需修改類的條件下替換依賴項的具體實現。spa

實現細節

一般 ServiceLocator 類提供 IServiceLocator 接口的實現單例,並負責管理該實例的建立和訪問。ServiceLocator 類提供 IServiceLocator 接口的默認實現,例如 ActivatingServiceLocator 類,能夠同時建立和定位服務。設計

注意事項

在使用 Service Locator 模式以前,請考慮如下幾點:3d

  • 有不少程序中的元素須要管理。
  • 在使用以前必須編寫額外的代碼將服務的引用添加到服務定位器。
  • 類將對服務定位器有依賴關係。
  • 源代碼變的更加複雜和難以理解。
  • 可使用配置數據來定義運行時的關係。
  • 必須提供服務的實現。由於服務定位器模式將服務消費者與服務提供者解耦,它可能須要提供額外的邏輯。這種邏輯將保證在服務消費者嘗試定位服務以前,服務提供者已被安裝和註冊。

相關模式

  • 依賴注入(Dependency Injection)。這種模式解決了與 Service Locator 模式相同的問題,但它使用不一樣的方法。
  • 控制反轉(Inversion of Control)。Service Locator 模式是這種模式的特殊版本。它將應用程序的傳統控制流程反轉。它用被調用對象來代替控制過程的調用方。

參考信息

代碼示例

Service Locator 的簡單實現,使用靜態類實現,未使用Singleton設計,僅做Mapping影射。版本控制

複製代碼
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;

namespace Infrastructure
{
  /// <summary>
  /// 服務定位器
  /// </summary>
  public static class ServiceLocator
  {
    #region Fields

    private static readonly Dictionary<Type, Type> _mapping 
    = new Dictionary<Type, Type>(); private static readonly Dictionary<Type, object> _resources
    = new Dictionary<Type, object>(); private static object _operationLock = new object(); #endregion #region Add /// <summary> /// 添加註冊資源 /// </summary> /// <typeparam name="TClass">資源類型</typeparam> /// <param name="instance">資源實例</param> public static void Add<TClass>(object instance) where TClass : class { Add(typeof(TClass), instance); } /// <summary> /// 添加註冊資源 /// </summary> /// <param name="typeOfInstance">資源類型</param> /// <param name="instance">資源實例</param> public static void Add(Type typeOfInstance, object instance) { if (typeOfInstance == null) throw new ArgumentNullException("typeOfInstance"); if (instance == null) throw new ArgumentNullException("instance"); if (!(typeOfInstance.IsInstanceOfType(instance))) { throw new InvalidCastException( string.Format(CultureInfo.InvariantCulture, "Resource does not implement supplied interface: {0}",
       typeOfInstance.FullName)); }
lock (_operationLock) { if (_resources.ContainsKey(typeOfInstance)) { throw new ArgumentException( string.Format(CultureInfo.InvariantCulture,
        "Resource is already existing : {0}", typeOfInstance.FullName)); } _resources[typeOfInstance] = instance; } } #endregion #region Get /// <summary> /// 查找指定類型的資源實例 /// </summary> /// <typeparam name="TClass">資源類型</typeparam> /// <returns>資源實例</returns> public static TClass Get<TClass>() where TClass : class { return Get(typeof(TClass)) as TClass; } /// <summary> /// 查找指定類型的資源實例 /// </summary> /// <param name="typeOfInstance">The type of instance.</param> /// <returns>資源實例</returns> public static object Get(Type typeOfInstance) { if (typeOfInstance == null) throw new ArgumentNullException("typeOfInstance"); object resource; lock (_operationLock) { if (!_resources.TryGetValue(typeOfInstance, out resource)) { throw new ResourceNotFoundException(typeOfInstance.FullName); } } if (resource == null) { throw new ResourceNotInstantiatedException(typeOfInstance.FullName); } return resource; } /// <summary> /// 嘗試查找指定類型的資源實例 /// </summary> /// <typeparam name="TClass">資源類型</typeparam> /// <param name="resource">資源實例</param> /// <returns>是否存在指定資源類型的資源實例</returns> public static bool TryGet<TClass>(out TClass resource) where TClass : class { bool isFound = false; resource = null; object target; lock (_operationLock) { if (_resources.TryGetValue(typeof(TClass), out target)) { resource = target as TClass; isFound = true; } } return isFound; } #endregion #region Register /// <summary> /// 註冊類型 /// </summary> /// <typeparam name="TClass">實體類型,類型限制爲有公共無參構造函數</typeparam> public static void RegisterType<TClass>() where TClass : class, new() { lock (_operationLock) { _mapping[typeof(TClass)] = typeof(TClass); } } /// <summary> /// 註冊類型 /// </summary> /// <typeparam name="TFrom">資源類型</typeparam> /// <typeparam name="TTo">實體類型,類型限制爲有公共無參構造函數</typeparam> public static void RegisterType<TFrom, TTo>() where TFrom : class where TTo : TFrom, new() { lock (_operationLock) { _mapping[typeof(TFrom)] = typeof(TTo); _mapping[typeof(TTo)] = typeof(TTo); } } /// <summary> /// 是否已註冊此類型 /// </summary> /// <typeparam name="TClass">資源類型</typeparam> /// <returns>是否已註冊此類型</returns> public static bool IsRegistered<TClass>() { lock (_operationLock) { return _mapping.ContainsKey(typeof(TClass)); } } #endregion #region Resolve /// <summary> /// 獲取類型實例 /// </summary> /// <typeparam name="TClass">資源類型</typeparam> /// <returns>類型實例</returns> public static TClass Resolve<TClass>() where TClass : class { TClass resource = default(TClass); bool existing = TryGet<TClass>(out resource); if (!existing) { ConstructorInfo constructor = null; lock (_operationLock) { if (!_mapping.ContainsKey(typeof(TClass))) { throw new ResourceNotResolvedException( string.Format(CultureInfo.InvariantCulture,
        "Cannot find the target type : {0}", typeof(TClass).FullName)); } Type concrete = _mapping[typeof(TClass)]; constructor = concrete.GetConstructor(
        BindingFlags.Instance | BindingFlags.Public, null, new Type[0], null); if (constructor == null) { throw new ResourceNotResolvedException( string.Format(CultureInfo.InvariantCulture,
        "Public constructor is missing for type : {0}", typeof(TClass).FullName)); } } Add<TClass>((TClass)constructor.Invoke(null)); } return Get<TClass>(); } #endregion #region Remove /// <summary> /// 移除指定類型的資源實例 /// </summary> /// <typeparam name="TClass">資源類型</typeparam> public static void Remove<TClass>() { Teardown(typeof(TClass)); } /// <summary> /// 移除指定類型的資源實例 /// </summary> /// <param name="typeOfInstance">資源類型</param> public static void Remove(Type typeOfInstance) { if (typeOfInstance == null) throw new ArgumentNullException("typeOfInstance"); lock (_operationLock) { _resources.Remove(typeOfInstance); } } #endregion #region Teardown /// <summary> /// 拆除指定類型的資源實例及註冊映射類型 /// </summary> /// <typeparam name="TClass">資源類型</typeparam> public static void Teardown<TClass>() { Teardown(typeof(TClass)); } /// <summary> /// 拆除指定類型的資源實例及註冊映射類型 /// </summary> /// <param name="typeOfInstance">資源類型</param> public static void Teardown(Type typeOfInstance) { if (typeOfInstance == null) throw new ArgumentNullException("typeOfInstance"); lock (_operationLock) { _resources.Remove(typeOfInstance); _mapping.Remove(typeOfInstance); } } #endregion #region Clear /// <summary> /// 移除全部資源 /// </summary> public static void Clear() { lock (_operationLock) { _resources.Clear(); _mapping.Clear(); } } #endregion } }
複製代碼

Service Locator 測試代碼

複製代碼
using System;
using Infrastructure;

namespace ServiceLocatorTest
{
  class Program
  {
    interface IServiceA
    {
      string GetData();
    }

    class ServiceA : IServiceA
    {
      public string GetData()
      {
        return "This data is from ServiceA";
      }
    }

    static void Main(string[] args)
    {
      ServiceLocator.RegisterType<IServiceA, ServiceA>();
      IServiceA serviceA = ServiceLocator.Resolve<IServiceA>();
      string data = serviceA.GetData();
      Console.WriteLine(data);
      Console.ReadKey();
    }
  }
}
複製代碼

相關文章
相關標籤/搜索