假设我有以下人为的代码:
abstract class Root
{
public abstract void PrintHierarchy();
}
class Level1 : Root
{
override public void PrintHierarchy()
{
Console.WriteLine("Level1 is a child of Root");
}
}
class Level2 : Level1
{
override public void PrintHierarchy()
{
Console.WriteLine("Level2 is a child of Level1");
base.PrintHierarchy();
}
}
如果我只看这个Level2
类,我可以立即看到它Level2.PrintHierarchy
遵循打开/关闭原则,因为它自己做一些事情并调用它覆盖的基本方法。
但是,如果我只看这个Level1
类,它似乎违反了 OCP,因为它不调用base.PrintHierarchy
——事实上,在 C# 中,编译器通过错误“无法调用抽象基成员”来禁止它。
使Level1
看起来遵循 OCP 的唯一方法是更改Root.PrintHierarchy
为空的虚拟方法,但是我不能再依赖编译器来强制派生类来实现PrintHierarchy
.
我在这里维护代码时遇到的真正问题是看到几十个override
方法不调用base.Whatever()
. 如果base.Whatever
是抽象的,那么很好,但如果不是,那么该Whatever
方法可能是被拉入接口而不是具体的可覆盖方法的候选方法——或者需要以其他方式重构类或方法,但要么方式,它清楚地表明设计不佳。
如果没有记住Root.PrintHierarchy
抽象的内容或在其中添加评论Level1.PrintHierarchy
,我是否还有其他选择可以快速确定形成的类Level1
是否违反 OCP?
评论中有很多很好的讨论,也有一些很好的答案。我很难弄清楚到底要在这里问什么。我认为让我感到沮丧的是,正如@Jon Hanna 指出的那样,有时虚拟方法只是表示“你必须实现我”,而其他时候它意味着“你必须扩展我——如果你没有调用基本版本,你破坏我的设计!” 但是 C# 没有提供任何方法来指示您的意思,除了抽象或接口显然是“必须实现”的情况。(除非代码合同中有一些东西,我认为这有点超出了范围)。
但是如果一种语言确实有一个必须实现与必须扩展的装饰器,如果它不能被禁用,它可能会给单元测试带来巨大的问题。有没有类似的语言?这听起来很像按合同设计,所以如果它在埃菲尔,例如,我不会感到惊讶。
最终结果可能就像@Jordão 所说的那样,而且完全是上下文相关的;但在我接受任何答案之前,我将让讨论开放一段时间。