重构静态类以将其接口与实现分离
本文关键字:实现 分离 接口 静态类 重构 | 更新日期: 2023-09-27 18:35:38
我正在开发一个基于 .NET 的应用程序,其中一些核心应用程序类仅使用静态方法设计。
用法示例:
// static access.
Parameters.GetValue("DefaultTimeout");
// static access.
Logger.Log("This is an important message!");
已经有使用这些静态方法的代码,所以这个"接口"不能改变。
这些类当前未实现任何接口。我希望能够将这些类的实际实现与其接口分开。
进行此重构的原因是这些对象将跨 AppDomain 边界使用。我希望能够注入一个"代理"对象,该对象在非主应用程序域上将调用其他一些实现而不是默认实现。
综上所述,我的问题是:
我如何才能轻松地转换仅静态访问基于接口的设计的对象,以便在需要时可以替换它们的实现(但保持静态访问)。
重构后,应该如何/何时实际注入非默认实现?
免责声明: 以下建议基于不更改主叫方的重要性。我并不是说这是最好的选择,只是我认为它是合适的。
断开实现的连接
无法在静态成员上具有接口,因此,如果您不想更改调用代码,则可能必须保留静态。也就是说,你可以简单地让你的静态类将一个接口包装在里面,所以静态类本身没有任何实现 - 它把所有调用委托给接口。
这一切都意味着你可以保留静态类和调用它的任何代码。这就像将静态类视为接口(或协定),但让它根据情况在内部交换实现。
这也意味着您的接口可以具有与静态类不同的签名,因为接口不必符合调用代码的期望 - 基本上,它会将您的静态类变成一种 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
--
现在,您可以添加支持相同声明的其他类,具有不同的实现方式。
干杯。
--