簡體   English   中英

重構靜態類以將其接口與實現分開

[英]Refactoring a static class to separate its interface from implementation

我正在開發一個基於.NET的應用程序,其中某些核心應用程序類僅使用靜態方法進行設計。

用法示例:

// static access.
Parameters.GetValue("DefaultTimeout");

// static access.
Logger.Log("This is an important message!");

那里已經有使用這些靜態方法的代碼,因此無法更改此“接口”。

這些類當前未實現任何接口。 我希望能夠將這些類的實際實現與它們的接口分開。

進行此重構的原因是,這些對象將在AppDomain邊界中使用。 我希望能夠注入一個“代理”對象,該對象在非main-appdomain上將調用其他一些實現,而不是默認的實現。

綜上所述,我的問題是:

  1. 如何通過僅靜態訪問基於接口的設計輕松地轉換對象,以便在需要時可以替換其實現(但保持靜態訪問)。

  2. 重構后,應如何/何時進行非默認實現的實際注入?

免責聲明:以下建議基於不更改主叫方的重要性。 我並不是說這是最佳選擇,只是我認為這是合適的。

斷開實施

無法在靜態成員上具有接口,因此,如果您不想更改調用代碼,則可能必須保留靜態。 就是說,您可以簡單地讓您的靜態類接口包裝在其中,因此靜態類本身沒有任何實現-將所有調用委派給該接口。

這一切都意味着您可以將靜態類和任何調用它的代碼保留在原處。 這就像將靜態類視為接口(或協定)一樣,但是可以根據情況在內部將實現換出。

這也意味着您的接口可以具有與靜態類不同的簽名,因為該接口不必符合調用代碼的期望-基本上,它將把您的靜態類轉變為Bridge

注入實施

簡而言之:使用靜態構造函數以解決此接口的給定實現。

靜態通常是每個AppDomain的(除非用ThreadStaticAttribute裝飾,然后是每個AppDomain / thread的),所以您可以基於當前AppDomain確定您所在的位置以及所需的實現(每次在AppDomain中首次使用靜態時,都會調用靜態構造函數) )。 這意味着,一旦構造完成,該特定靜態類的包裝實現將在AppDomain的時間內保留(盡管您可以實現刷新實現的方法)。

跨應用程序域調用

負責此操作的代碼可以在靜態類中,也可以使接口實現之一只是AppDomain類型的代理管理器。 交叉AppDomain調用的任何類型都需要繼承MarshalByRefObject

http://msdn.microsoft.com/en-us/library/ms173139.aspx

在另一個AppDomain中的類型的CreateInstance

進行跨應用程序域調用的最簡單方法?

樣品申請

您應該只能夠將其復制並粘貼到新的控制台應用程序中。 這是在為默認的AppDomain注冊一個實現,為用戶制作的AppDomains注冊一個實現。 默認值只是創建該接口的遠程實現(在另一個AppDomain中)。 只是為了演示“每個AppDomain靜態”的想法,遠程實現將委派給非默認域的另一個實現。

您可以隨時更改實現,您只需更改靜態類構造函數即可(決定選擇哪種實現)。 請注意,您無需更改Main方法(在這種情況下為我們的調用代碼)。

using System;
using System.Reflection;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);
        Console.WriteLine(Parameters.GetValue(""));
        Console.Read();
    }
}

static class Parameters
{
    private static IParameterProvider _provider;

    static Parameters()
    {
        if (AppDomain.CurrentDomain.IsDefaultAppDomain())
        {
            _provider = new ParameterProviderProxy(AppDomain.CreateDomain(Guid.NewGuid().ToString()));
        }
        else
        {
            // Breakpoint here to see the non-default AppDomain pick an implementation.
            _provider = new NonDefaultParameterProvider();
        }
    }

    public static object GetValue(string name)
    {
        return _provider.GetValue(name);
    }
}

interface IParameterProvider
{
    object GetValue(string name);
}

class CrossDomainParameterProvider : MarshalByRefObject, IParameterProvider
{
    public object GetValue(string name)
    {
        return Parameters.GetValue(name);
    }
}

class NonDefaultParameterProvider : IParameterProvider
{
    public object GetValue(string name)
    {
        return AppDomain.CurrentDomain.FriendlyName;
    }
}

class ParameterProviderProxy : IParameterProvider
{
    private IParameterProvider _remoteProvider;

    public ParameterProviderProxy(AppDomain containingDomain)
    {
        _remoteProvider = (CrossDomainParameterProvider)containingDomain.CreateInstanceAndUnwrap(
            Assembly.GetExecutingAssembly().FullName,
            typeof(CrossDomainParameterProvider).FullName);
    }

    public object GetValue(string name)
    {
        return _remoteProvider.GetValue(name);
    }
}

關於壽命的注釋

管理靜態類的重構的主要問題之一通常不是更改客戶端代碼(因為許多重構工具都支持這種更改,並且有一些技術可以安全地完成它),而是管理該類的生命周期。賓語。 實例對象依賴於活動的引用(否則它們是垃圾收集的),通常可以通過將它們放在某個地方的公共靜態成員中來使它們“易於訪問”,但是通常這是您首先要通過重構來避免的事情。

似乎不必擔心此問題,因為您將調用代碼附加在靜態類上,因此壽命將保持不變。

為每個靜態方法創建一個實例。 添加一個靜態單例變量,您可以將其分配給任何實現。 使靜態方法在靜態單例上調用實例方法。

這將允許您在運行時交換實現,但是您只能同時掛接一個實現。

現有代碼無需更改。

靜態類可以轉換為Singleton對象。

Singleton Objects支持接口。

接口可以用於不同的實現。

(1)問題的定義。

假設您有一個具有靜態成員的類。

-

StringsClass.cs

-

namespace Libraries
{
  public static class StringsClass
  {

    public static string UppercaseCopy(string Value)
    {
      string Result = "";

      // code where "Value" is converted to uppercase,
      // and output stored in "Result"

    return Result;
    } // string UppercaseCopy(...)

    public static string LowercaseCopy(string Value)
    {
      string Result = "";

      // code where "Value" is converted to lowercase,
      // and output stored in "Result"

      return Result;
    } // string LowercaseCopy(...)

    public static string ReverseCopy(string Value)
    {
      string Result = "";

      // code where "Value" is reversed,
      // and output stored in "Result"

      return Result;
    } // string ReverseCopy(...)  

  } // class StringsClass

} // namespace Libraries

-

並且,使用該類中的靜態元素的一些代碼。

-

StringsLibraryUser.cs

-

using Libraries;

namespace MyApp
{
  public class AnyClass
  {
    public void AnyMethod()
    {
      string Example = "HELLO EARTH";
      string AnotherExample = StringsClass.LowercaseCopy(Example);
    } // void AnyMethod(...)  

  } // class AnyClass

} // namespace MyApp

-

(2)首先將類轉換為非靜態類。

-

StringsClass.cs

-

namespace Libraries
{
  public class StringsClass
  {

    public string UppercaseCopy(string Value)
    {
      string Result = "";

      // code where "Value" is converted to uppercase,
      // and output stored in "Result"

    return Result;
    } // string UppercaseCopy(...)

    public string LowercaseCopy(string Value)
    {
      string Result = "";

      // code where "Value" is converted to lowercase,
      // and output stored in "Result"

      return Result;
    } // string LowercaseCopy(...)

    public string ReverseCopy(string Value)
    {
      string Result = "";

      // code where "Value" is reversed,
      // and output stored in "Result"

      return Result;
    } // string ReverseCopy(...)  

  } // class StringsClass

} // namespace Libraries

-

(3)添加代碼允許類處理單個對象。

-

StringsClass.cs

-

namespace Libraries
{
  public class StringsClass
  {
    private static Singleton instance = null;

    private Singleton()
    {
      // ...
    }

    public static synchronized Singleton getInstance()
    {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    public string UppercaseCopy(string Value)
    {
      string Result = "";

      // code where "Value" is converted to uppercase,
      // and output stored in "Result"

    return Result;
    } // string UppercaseCopy(...)

    public string LowercaseCopy(string Value)
    {
      string Result = "";

      // code where "Value" is converted to lowercase,
      // and output stored in "Result"

      return Result;
    } // string LowercaseCopy(...)

    public string ReverseCopy(string Value)
    {
      string Result = "";

      // code where "Value" is reversed,
      // and output stored in "Result"

      return Result;
    } // string ReverseCopy(...)  

  } // class StringsClass

} // namespace Libraries

-

(4)調用該類的代碼,應添加單例的引用。

-

StringsLibraryUser.cs

-

using Libraries;

namespace MyApp
{
  public class AnyClass
  {
    public void AnyMethod()
    {
      string Example = "HELLO EARTH";
      string AnotherExample = StringsClass.getInstance().LowercaseCopy(Example);
    } // void AnyMethod(...)  

  } // class AnyClass

} // namespace MyApp

-

(5)定義一個接口,使用與先前的靜態類相似的聲明,並允許單例實現該接口。 在接口聲明中省略單例成員

-

StringsClass.cs

-

namespace Libraries
{
  public interface StringsInterface
  {
    string UppercaseCopy(string Value);  
    string LowercaseCopy(string Value);  
    string ReverseCopy(string Value);   
  } // interface StringsInterface

  public class StringsClass: StringsInterface
  {
    private static Singleton instance = null;

    private Singleton()
    {
      // ...
    }

    public static synchronized Singleton getInstance()
    {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    public string UppercaseCopy(string Value)
    {
      string Result = "";

      // code where "Value" is converted to uppercase,
      // and output stored in "Result"

    return Result;
    } // string UppercaseCopy(...)

    public string LowercaseCopy(string Value)
    {
      string Result = "";

      // code where "Value" is converted to lowercase,
      // and output stored in "Result"

      return Result;
    } // string LowercaseCopy(...)

    public string ReverseCopy(string Value)
    {
      string Result = "";

      // code where "Value" is reversed,
      // and output stored in "Result"

      return Result;
    } // string ReverseCopy(...)  

  } // class StringsClass

} // namespace Libraries

-

(6)在代碼中,如果您使用的是單例(包含靜態方法的上一類),請替換接口的單例。

-

StringsLibraryUser.cs

-

using Libraries;

namespace MyApp
{
  public class AnyClass
  {
    public StringsInterface StringsHelper = StringsClass.getInstance().LowercaseCopy(Example);

    public void AnyMethod()
    {
      string Example = "HELLO EARTH";
      string AnotherExample = StringsHelper;
    } // void AnyMethod(...)  

  } // class AnyClass

} // namespace MyApp

-

現在,您可以添加其他支持相同聲明和不同實現的類。

干杯。

-

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM