重构静态类以将其接口与实现分离

本文关键字:实现 分离 接口 静态类 重构 | 更新日期: 2023-09-27 18:35:38

我正在开发一个基于 .NET 的应用程序,其中一些核心应用程序类仅使用静态方法设计。

用法示例:

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

已经有使用这些静态方法的代码,所以这个"接口"不能改变。

这些类当前未实现任何接口。我希望能够将这些类的实际实现与其接口分开。

进行此重构的原因是这些对象将跨 AppDomain 边界使用。我希望能够注入一个"代理"对象,该对象在非主应用程序域上将调用其他一些实现而不是默认实现。

综上所述,我的问题是:

  1. 我如何才能轻松地转换仅静态访问基于接口的设计的对象,以便在需要时可以替换它们的实现(但保持静态访问)。

  2. 重构后,应该如何/何时实际注入非默认实现?

重构静态类以将其接口与实现分离

免责声明: 以下建议基于不更改主叫方的重要性。我并不是说这是最好的选择,只是我认为它是合适的。

断开实现的连接

无法

在静态成员上具有接口,因此,如果您不想更改调用代码,则可能必须保留静态。也就是说,你可以简单地让你的静态类将一个接口包装在里面,所以静态类本身没有任何实现 - 它把所有调用委托给接口。

这一切都意味着你可以保留静态类和调用它的任何代码。这就像将静态类视为接口(或协定),但让它根据情况在内部交换实现。

这也意味着您的接口可以具有与静态类不同的签名,因为接口不必符合调用代码的期望 - 基本上,它会将您的静态类变成一种 Bridge。

注入实现

简而言之:使用静态构造函数来解析此接口的给定实现。

静态通常是每个 AppDomain 的(除非用 ThreadStaticAttribute 修饰,然后是每个 AppDomain/线程),因此您可以根据当前 AppDomain 确定您所在的位置以及所需的实现(每当在 AppDomain 中首次使用静态时,将调用静态构造函数)。这意味着,一旦构造,该特定静态类的包装实现将在 AppDomain 的持续时间内保留(尽管您可以实现方法来刷新实现)。

跨应用域调用

负责此操作的代码可以位于静态类中,也可以使其中一个接口实现只是 AppDomain 类型的代理管理器。跨应用程序域调用的任何类型都需要继承MarshalByRefObject

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

创建另一个应用程序域中类型的实例

进行跨应用域调用的最简单方法?

示例应用程序

您应该能够将其复制并粘贴到新的控制台应用程序中。这样做是为默认 AppDomain 注册一个实现,为用户创建的 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);
    }
}

关于寿命的说明

管理

静态类重构的主要问题之一通常不是客户端代码的更改(因为许多重构工具都支持这样做,并且有一些技术可以安全地完成它),而是管理对象的生命周期。实例对象依赖于活引用(否则它们会被垃圾回收),这些对象通常可以通过在某个地方的公共静态成员中保留一个来"轻松访问",但通常这是您首先试图通过重构来避免的。

您似乎不必担心这个问题,因为您将调用代码附加到静态类,因此生命周期将保持不变。

对于每个静态方法,创建一个实例。添加可以向其分配任何实现的静态单例变量。使静态方法调用静态单一实例上的实例方法。

这将允许您在运行时交换实现,但您只能同时挂接一个实现。

现有代码不需要更改。

静态类可以转换为单例对象。

单一实例对象支持接口。

接口可用于不同的实现。

(1)问题的定义。

假设您有一个具有静态成员的类。

--

字符串类.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

--

并且,来自该类的几个使用该静态元素的代码。

--

字符串库用户.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)首先将类转换为非静态类。

--

字符串类.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) 添加代码,允许类处理单个对象。

--

字符串类.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)调用类的代码,应添加对单例的引用。

--

字符串库用户.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)定义一个接口,具有与以前的静态类类似的声明,并允许单一实例实现该接口。在接口声明中省略单例成员

--

字符串类.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) 在代码中,你正在使用你的单例,即包含静态方法的上一个类,替换接口的单例。

--

字符串库用户.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

--

现在,您可以添加支持相同声明的其他类,具有不同的实现方式。

干杯。

--