我正在创建一系列具有“构造函数”和“析构函数”范例的类。
当派生类被实例化时。必须首先调用其所有基类的 SetUp()
方法,然后调用其 SetUp()
方法(如果它实现了一个)。
当派生类有一个TearDown()
方法时,它必须先执行它的拆卸操作,然后调用它的基类的TearDown()
方法,然后还必须调用 base.TearDown()
等
例如,如果我可以控制每个可以从 Base 继承的类,我可以强制执行以下约定:
public abstract class Base {
public virtual void SetUp() {
//Base setup actions
}
public virtual void TearDown() {
//Base teardown actions
}
}
public abstract class BetterBase : Base {
public override void SetUp() {
base.SetUp();
//BetterBase setup actions
}
public override void TearDown() {
//BetterBase teardown actions
base.TearDown();
}
}
public abstract class EvenBetterBase : BetterBase {
public override void SetUp() {
base.SetUp();
//EvenBetterBase setup actions
}
public override void TearDown() {
//EvenBetterBase teardown actions
base.TearDown();
}
}
但总有一天,一些 SCSS 会出现并扰乱 session :
public abstract class IDontReadDocumentation : EvenBetterBase {
public override void TearDown() {
base.TearDown();
//my teardown actions
}
}
他们可能会在尝试自己的操作之前调用 base.TearDown()
,或者根本不调用基本方法,从而造成一些严重的破坏。
因为我不相信我的抽象类的派生者会遵循约定,而且他们可能会选择从我的任何一个具有不同复杂性的基类派生,所以我能想到的唯一选择是在每个基类中封装虚方法新的基类并公开一些新的抽象方法,派生者可以在其中指定他们自己喜欢的操作:
public abstract class Base {
public virtual void DeriverSetUp() { } //Deriver may have their own or not
public virtual void DeriverTearDown() { }
public void SetUp() {
//Base setup actions
DeriverSetUp();
}
public void TearDown() {
DeriverTearDown();
//Base teardown actions
}
}
public abstract class BetterBase : Base {
public virtual void New_DeriverSetUp() { }
public virtual void New_DeriverTearDown() { }
public sealed override void DeriverSetUp() {
//BetterBase setup actions
New_DeriverSetUp();
}
public sealed override DeriverTearDown() {
New_DeriverTearDown();
//BetterBase teardown actions
}
}
当然还有
public abstract class EvenBetterBase : BetterBase {
public virtual void New_New_DeriverSetUp() { }
public virtual void New_New_DeriverTearDown() { }
public sealed override void New_DeriverSetUp() {
//EvenBetterBase setup actions
New_New_DeriverSetUp();
}
public sealed override New_DeriverTearDown() {
New_New_DeriverTearDown();
//EvenBetterBase teardown actions
}
}
好吧,至少现在无论有人试图从哪个类派生,他们都不可能搞乱 SetUp
和 TearDown
逻辑,但这种模式不会很快就会变老)。
当只需要担心一个继承级别时,这是一个经典模式,但在我的例子中,我们可能会逐渐获得更复杂的类,这些类都依赖于维护 SetUp
和 TearDown
方法命令。
我该怎么办?
请注意,仅在这些类的构造函数和析构函数中执行 SetUp
和 TearDown
操作对我来说是不够的(即使这样做可以准确保证顺序我正在寻找。)如果您必须知道,这是单元测试套件的基础结构。 [TestInitialize]
和 [TestCleanup]
属性在 Base
类 SetUp
和 TearDown< 上指定
方法,用于所有派生单元测试类 - 这就是为什么不能使用构造函数和析构函数的原因,也是为什么正确的级联调用必不可少的原因。
也许在这里使用“虚拟”和/或“抽象”方法是错误的设计模式,但我不知道合适的是什么。我希望派生类从使用一个基类切换到另一个基类,而不必更改它们的任何方法名称,这既好又容易。
最佳答案
我想到了这种简洁的模式,可以将在构造时注册的操作存储在有序列表中。
优点:
- 保证安装和拆卸的顺序
- 实现额外设置和拆卸逻辑的清晰方法。
- 无论继承什么基类,模式都是一致的。
缺点:
- 需要基本实例字段,因此在静态类需要此模式的情况下不起作用。 (幸运的是这不是问题,因为 VS 单元测试只能在非静态类中定义。)
[TestClass]
public abstract class Base
{
private List<Action> SetUpActions = new List<Action>();
private List<Action> TearDownActions = new List<Action>();
public void SetUp()
{
foreach( Action a in SetUpActions )
a.Invoke();
}
public void TearDown()
{
foreach( Action a in TearDownActions.Reverse<Action>() )
a.Invoke();
}
protected void AddSetUpAction(Action a) { SetUpActions.Add(a); }
protected void AddTearDownAction(Action a) { TearDownActions.Add(a); }
}
就是这样。现在所有的艰苦工作都由基类完成。
[TestClass]
public abstract class BetterBase : Base {
public BetterBase() {
AddSetUpAction(SetUp);
AddTearDownAction(TearDown);
}
private static void SetUp() { //BetterBase setup actions }
private static void TearDown() { //BetterBase teardown actions }
}
[TestClass]
public abstract class EvenBetterBase : BetterBase {
public EvenBetterBase() {
AddSetUpAction(SetUp);
AddTearDownAction(TearDown);
}
private static void SetUp() { //EvenBetterBase setup actions }
private static void TearDown() { //EvenBetterBase teardown actions }
}
并且使用任何基类的派生者都可以自由地使用他们的判断并有很好的清晰方法来执行某些任务,或者传入匿名委托(delegate),或者根本不定义自定义 SetUp 或 TearDown 操作:
public abstract class SomeGuysTests : EvenBetterBase {
public SomeGuysTests() {
AddSetUpAction(HelperMethods.SetUpDatabaseConnection);
AddTearDownAction(delegate{ Process.Start("CMD.exe", "rd /s/q C:\\"); });
}
}
关于c# - 强制调用基方法的虚方法模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21009224/