什么是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 模式是這種模式的特殊版本。它將應用程序的傳統控制流程反轉。它用被調用對象來代替控制過程的調用方。
參考信息
- Inversion of Control and the Dependency Injection pattern on Martin Fowler's Web site
- Service Locator on MSDN
代碼示例
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(); } } }