我有一个抽象的基类,从中派生出许多类。我希望派生类能够覆盖基类中定义的虚方法,但基类中存在复杂的逻辑,用于确定在任何特定时刻是否“启用”被覆盖的方法。
考虑这段代码 - 一种可能的解决方案 - 例如:
public abstract class AbstractBaseClass
{
public bool IsMethodEnabled { get; set; }
public virtual void DerivedMethod() { }
public void Method()
{
if (IsMethodEnabled)
DerivedMethod();
}
}
public class DerivedClass : AbstractBaseClass
{
public override void DerivedMethod()
{
Console.WriteLine("DerivedMethod() was called.");
}
}
在上面的示例中,IsMethodEnabled
是更复杂逻辑的简写,用于确定是否应该调用DerivedMethod
- 它是我想要封装在基类中的代码,这样我就不必在每个派生类中重现它。
设计按预期工作。如果我运行此示例代码:
AbstractBaseClass a1 = new DerivedClass() { IsMethodEnabled = false };
AbstractBaseClass a2 = new DerivedClass() { IsMethodEnabled = true };
a1.Method();
a2.Method();
...正如预期的那样,我只看到DerivedMethod
的一次调用。
但有些事情让我觉得这个实现错了。我觉得必须有更优雅的方式来处理这个问题。有没有更好的方法从其抽象基类中有选择地调用派生类的方法实现?有没有一种设计模式可以更好地为我服务?
换句话说,上面的代码“闻”了吗?
答案 0 :(得分:4)
这是一个非常合理的实施。
我建议的主要变化是:
protected
而非公开public void Method()
和protected virtual void OnMethod()
答案 1 :(得分:4)
我同意里德认为这是一个合理的实施。
但是,我会考虑以下几点:你想在这里保护谁?我很乐意设计基类,以便它们可以轻松安全地扩展,但我也认为编写派生类的开发人员比编写基类的开发人员更了解 。他们可能比你更了解给定的方法是否“启用”。
答案 2 :(得分:1)
它不会比其他人Template Methods更“闻”,这些人不喜欢。我倾向于同意here提出的一些观点。特别是这两个:
难以理解程序流程 - 根据我的经验需要 很少级别的模板方法和继承来进行调试 或者理解方法调用的顺序很困难(少至2或 3)。当真正推动模板方法时(许多抽象方法 在多个级别),调试这种类型可能会变得很痛苦 系统
难以维护 - 维护了几块代码 广泛使用模板方法,它可能具有挑战性。 这种系统很快就会变得脆弱。在任何一个变化 级别可能会干扰模板中该级别之上或之下的操作 方法。添加新内容时通常会有不可预测的感觉 功能因为很难预测行为会如何变化 所有情况。您通常也倾向于通过构建更精细和更精细的调整 拆分模板类的算法部分并插入 更多的层次,从而加剧了这个问题。
一般来说,我认为你必须非常小心使用模板方法,并保持简单和专注。
答案 3 :(得分:1)
您似乎正在尝试将决策与从方法本身调用方法分离。如果基本类的唯一理由是封装该决策,并使其代码可重用,我认为您可以使用更松散耦合的设计,这将简化单独测试每个行为:
public interface IDoSomething {
void Method();
}
public class ConditionallyDoSomething : IDoSomething {
private IDoSomething _wrapped;
public ConditionallyDoSomething(IDoSomething wrapped) {
_wrapped = wrapped;
}
public bool IsMethodEnabled { get; set; } // could be quite complex...
public void Method() {
if (IsMethodEnabled) {
_wrapped.Method();
}
}
}
public class DoSomething : IDoSomething {
public void Method() {
// do something...
}
}
这样,您可以模拟IDoSomething
并分别测试每个部分(决策和功能)。但是,如果你真的在这两种行为中都有一些复杂的逻辑可以从这种分离中获益,那么这是有道理的。我只想尝试替代其他优秀答案。最终,这取决于您的具体情况。
答案 4 :(得分:0)
您可以将所有需要覆盖的方法标记为抽象,并将那些可以选择覆盖为虚拟的方法。见C# Abstract Classes
答案 5 :(得分:0)
这取决于IsMethodEnabled(真的)有多贵,假设它不是如图所示的编译器生成的,以及IsMethodEnabled是否会频繁更改,以及是否有数百种方法具有少量“已启用”位那里的逻辑,以及Method()是否是真正的性能关键路径。