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

lys*_*cid 8 .net c# oop refactoring

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

用法示例:

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

// static access.
Logger.Log("This is an important message!");
Run Code Online (Sandbox Code Playgroud)

已经存在使用这些静态方法的代码,因此无法更改此"接口".

这些类目前没有实现接口.我希望能够将这些类的实际实现与其接口分开.

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

总之,我的问题是:

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

  2. 一旦重构,应该如何/ WHEN实际注入非默认实现?

Ada*_*rth 11

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

断开实施

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

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

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

注入实施

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

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

跨AppDomain呼叫

负责此操作的代码可以在静态类中,也可以将其中一个接口实现简单地作为AppDomain类型的代理管理器.任何类型的跨AppDomain调用都需要继承MarshalByRefObject.

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

另一个AppDomain中的Type的CreateInstance

最简单的方法来进行跨appdomain调用?

样品申请

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

关于生命跨度的一个注记

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

看起来您不必担心这种担忧,因为您将调用代码附加到静态类,因此寿命将保持不变.


usr*_*usr 5

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

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

现有代码不需要更改.