c# - 重构静态类以将其接口(interface)与实现分离

标签 c# .net oop refactoring

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

示例用法:

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

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

已经有使用这些静态方法的代码,因此无法更改此“接口(interface)”。

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

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

总而言之,我的问题是:

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

  2. 重构后,非默认实现的实际注入(inject)应该如何/何时发生?

最佳答案

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

断开实现

没有办法在静态成员上拥有接口(interface),因此如果您不想更改调用代码,静态成员可能必须保留。也就是说,您可以简单地在静态类中包装一个接口(interface),因此静态类本身没有任何实现 - 它将所有调用委托(delegate)给接口(interface)。

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

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

注入(inject)实现

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

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

跨应用域调用

负责此的代码可以在静态类中,也可以使接口(interface)实现之一简单地成为 AppDomain 类型的代理管理器。跨 AppDomain 调用的任何类型都需要继承 MarshalByRefObject

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

CreateInstance of a Type in another AppDomain

Simplest way to make cross-appdomain call?

示例应用程序

您应该能够将其复制并粘贴到新的控制台应用程序中。这是在为默认的 AppDomain 和用户创建的 AppDomains 注册一个实现。默认只是创建接口(interface)的远程实现(在另一个 AppDomain 中)。只是为了演示“每个 AppDomain 静态”的想法,远程实现委托(delegate)给非默认域的另一个实现。

您可以即时更改实现,您需要更改的只是静态类构造函数(决定选择什么实现)。请注意,您无需更改 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);
    }
}

关于生命周期的注释

管理静态类重构的主要问题之一通常不是更改客户端代码(因为许多重构工具都支持这一点,并且有一些技术可以安全地完成它),而是管理生命对象的跨度。实例对象依赖于事件引用(否则它们会被垃圾收集),通常可以通过将一个对象保存在某个公共(public)静态成员中来使它们“易于访问”,但通常这是您首先要通过重构来避免的。

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

关于c# - 重构静态类以将其接口(interface)与实现分离,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10775534/

相关文章:

c# - 我们可以在 .net 网站中集成 Napster 吗?

c# - WPF 图像处理

.net - 将 Excel 文件读入 .NET 应用程序 : ADO. 网络或 Microsoft.Office.Interop.Excel.Application 的更快方法是什么?

c# - 如何在不同型号的 Controller 之间共享代码?

c# - 将 XML 文档转换为流利的 C#

.net - 在 VBScript (COM-interop) 中确定 .Net 方法后缀号

jquery - 在 jQuery 中扩展原型(prototype)时如何保持对 this 关键字的控制?

java - 观察者模式建议

c++ - 在另一个头文件 C++ 中创建一个类的实例

c# - 将 html 转换为 word