Service Locator 模式

lotus_ruan發表於2021-09-09

什麼是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
{
  /// 
  /// 服務定位器
  /// 
  public static class ServiceLocator
  {
    #region Fields

    private static readonly Dictionary _mapping 
    = new Dictionary();
    private static readonly Dictionary _resources 
    = new Dictionary();
    private static object _operationLock = new object();

    #endregion

    #region Add

    /// 
    /// 新增註冊資源
    /// 
    /// 資源型別
    /// 資源例項
    public static void Add(object instance)
        where TClass : class
    {
      Add(typeof(TClass), instance);
    }

    /// 
    /// 新增註冊資源
    /// 
    /// 資源型別
    /// 資源例項
    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

    /// 
    /// 查詢指定型別的資源例項
    /// 
    /// 資源型別
    /// 資源例項
    public static TClass Get()
        where TClass : class
    {
      return Get(typeof(TClass)) as TClass;
    }

    /// 
    /// 查詢指定型別的資源例項
    /// 
    /// The type of instance.
    /// 資源例項
    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;
    }

    /// 
    /// 嘗試查詢指定型別的資源例項
    /// 
    /// 資源型別
    /// 資源例項
    /// 是否存在指定資源型別的資源例項
    public static bool TryGet(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

    /// 
    /// 註冊型別
    /// 
    /// 實體型別,型別限制為有公共無參建構函式
    public static void RegisterType()
      where TClass : class, new()
    {
      lock (_operationLock)
      {
        _mapping[typeof(TClass)] = typeof(TClass);
      }
    }

    /// 
    /// 註冊型別
    /// 
    /// 資源型別
    /// 實體型別,型別限制為有公共無參建構函式
    public static void RegisterType()
      where TFrom : class
      where TTo : TFrom, new()
    {
      lock (_operationLock)
      {
        _mapping[typeof(TFrom)] = typeof(TTo);
        _mapping[typeof(TTo)] = typeof(TTo);
      }
    }

    /// 
    /// 是否已註冊此型別
    /// 
    /// 資源型別
    /// 是否已註冊此型別
    public static bool IsRegistered()
    {
      lock (_operationLock)
      {
        return _mapping.ContainsKey(typeof(TClass));
      }
    }

    #endregion

    #region Resolve

    /// 
    /// 獲取型別例項
    /// 
    /// 資源型別
    /// 型別例項
    public static TClass Resolve()
      where TClass : class
    {
      TClass resource = default(TClass);

      bool existing = TryGet(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)constructor.Invoke(null));
      }

      return Get();
    }

    #endregion

    #region Remove

    /// 
    /// 移除指定型別的資源例項
    /// 
    /// 資源型別
    public static void Remove()
    {
      Teardown(typeof(TClass));
    }

    /// 
    /// 移除指定型別的資源例項
    /// 
    /// 資源型別
    public static void Remove(Type typeOfInstance)
    {
      if (typeOfInstance == null)
        throw new ArgumentNullException("typeOfInstance");

      lock (_operationLock)
      {
        _resources.Remove(typeOfInstance);
      }
    }

    #endregion

    #region Teardown

    /// 
    /// 拆除指定型別的資源例項及註冊對映型別
    /// 
    /// 資源型別
    public static void Teardown()
    {
      Teardown(typeof(TClass));
    }

    /// 
    /// 拆除指定型別的資源例項及註冊對映型別
    /// 
    /// 資源型別
    public static void Teardown(Type typeOfInstance)
    {
      if (typeOfInstance == null)
        throw new ArgumentNullException("typeOfInstance");

      lock (_operationLock)
      {
        _resources.Remove(typeOfInstance);
        _mapping.Remove(typeOfInstance);
      }
    }

    #endregion

    #region Clear

    /// 
    /// 移除所有資源
    /// 
    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 = ServiceLocator.Resolve();
      string data = serviceA.GetData();
      Console.WriteLine(data);
      Console.ReadKey();
    }
  }
}

圖片描述

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1762/viewspace-2800869/,如需轉載,請註明出處,否則將追究法律責任。

相關文章