8

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

示例用法:

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

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

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

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

此重构的原因是这些对象将跨 AppDomain 边界使用。我希望能够注入一个“代理”对象,该对象在非 main-appdomains 上将调用其他一些实现而不是默认实现。

总结一下,我的问题是:

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

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

4

3 回答 3

12

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

断开实施

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

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

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

注入实现

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

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

跨 AppDomain 调用

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

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

在另一个 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);
    }
}

关于寿命的说明

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

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

于 2012-05-27T17:10:49.303 回答
6

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

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

现有代码不需要更改。

于 2012-05-27T17:01:51.603 回答
3

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

单例对象支持接口。

接口可用于不同的实现。

(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

--

并且,一些使用该类的静态元素的代码。

--

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) 首先将类转换为非静态类。

--

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

--

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) 定义一个接口,声明与前面的静态类类似,并允许单例实现该接口。在接口声明中省略单例成员

--

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

--

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

干杯。

--

于 2012-05-27T20:03:54.847 回答