抽象方法和开放原理

假设我有以下设计代码:
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
。 我在维护代码时遇到的真正问题是看到几十个不调用
base.Whatever()
的方法。如果
base.Whatever
是抽象的,那么很好,但如果不是,那么
Whatever
方法可能是被拉入接口的候选者而不是具体的可覆盖方法 - 或者类或方法需要以其他方式重构,但无论哪种方式,它都清楚地表明设计不佳。 如果没有记住
Root.PrintHierarchy
是抽象的或在
Level1.PrintHierarchy
内置评论,我是否有其他选择来快速确定类似
Level1
的类是否违反了OCP? 评论中有很多很好的讨论,也有一些很好的答案。我无法弄清楚到底要问什么。我认为让我感到沮丧的是,正如@Jon Hanna指出的那样,有时虚拟方法只是表示“你必须实现我”,而有时它意味着“你必须扩展我 - 如果你没有调用基础版本,你打破我的设计!“但是C#没有提供任何方式来表明你的意思,除了那个抽象或界面显然是一个“必须实现”的情况。 (除非Code Code中有一些东西,我认为这有点超出范围)。 但是,如果一种语言确实具有必须实现与必须扩展的装饰器,那么如果它不能被禁用,它可能会为单元测试带来巨大的问题。有这样的语言吗?这听起来很像设计合同,所以如果它在艾菲尔铁塔,我也不会感到惊讶。 最终的结果可能就像@Jordão所说的那样,而且完全是上下文的;但在我接受任何答案之前,我将暂时搁置讨论。     
已邀请:
Root
定义以下内容:根对象具有PrintHierarchy方法。它没有定义关于PrintHierarchy方法的更多信息。
Level1
有一个PrintHierarchy方法。它不会停止使用PrintHierarchy方法,因此它绝不违反开放/封闭原则。 现在,更重要的是:将“PrintHierarchy”重命名为“Foo”。 Level2是否遵循或违反开放/封闭原则? 答案是我们没有线索,因为我们不知道“Foo”的语义是什么。因此,我们不知道是否应该在方法体的其余部分之后,在休息之前,在其中间或根本不调用base.Foo。 应该
1.ToString()
返回“System.ObjectSystem.ValueType1”或“1System.ValueTypeSystem.Object”来保持打开/关闭的假装,还是应该在返回“1”之前将调用分配给
base.ToString()
到未使用的变量? 显然没有这些。它应尽可能返回有意义的字符串。它的基类型尽可能地返回有意义的字符串,并且扩展它不会受益于对其基数的调用。 打开/关闭原则意味着当我调用Foo()时,我希望发生一些Fooing,并且当我在Level1上调用它时我期望一些Level1适当的Fooing,而当我在Level2上调用它时,我期望一些Level2适当的Fooing。 Level2 Fooing是否应该涉及一些Level1 Fooing也会发生取决于Fooing是什么。
base
是一个帮助我们扩展课程而不是要求的工具。     
您无法通过静态查看系统(或类)来确定系统(或类)是否遵循OCP,而没有上下文信息。 只有当您知道设计可能发生的变化时,才能判断它是否遵循OCP进行这些特定类型的变更。 没有语言结构可以帮助你。 一个好的启发式方法是使用某种度量标准,如罗伯特·马丁的不稳定性和抽象性(pdf),或者缺乏凝聚力的指标,以便更好地为变革做好准备,并有更好的机会跟随OCP和所有其他OOD的重要原则。     
OCP完全是关于先决条件和后置条件的:“当你只能用较弱的一个替换它的前提条件,而用一个更强的条件替换它的后置条件”。 我不认为在重写方法中调用base会违反它。     

要回复问题请先登录注册