抽象方法和开放原理

Mar*_*off 7 c# oop open-closed-principle

假设我有以下设计代码:

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

如果我只是在看这个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#没有提供任何方式来表明你的意思,除了那个抽象或界面显然是一个"必须实现"的情况.(除非Code Code中有一些东西,我认为这有点超出范围).

但是,如果一种语言确实具有必须实现与必须扩展的装饰器,那么如果它不能被禁用,它可能会为单元测试带来巨大的问题.有这样的语言吗?这听起来很像设计合同,所以如果它在埃菲尔,我也不会感到惊讶.

最终的结果可能就像@Jordão所说的那样,而且完全是上下文的; 但在我接受任何答案之前,我将暂时搁置讨论.

Jon*_*nna 5

Root定义以下内容:根对象具有PrintHierarchy方法.它没有定义关于PrintHierarchy方法的更多信息.

Level1有一个PrintHierarchy方法.它不会停止使用PrintHierarchy方法,因此它绝不违反开放/封闭原则.

现在,更重要的是:将"PrintHierarchy"重命名为"Foo".Level2是否遵循或违反开放/封闭原则?

答案是我们没有线索,因为我们不知道"Foo"的语义是什么.因此,我们不知道是否应该在方法体的其余部分之后,在休息之前,在其中间或根本不调用base.Foo.

应该1.ToString()返回"System.ObjectSystem.ValueType1"或"1System.ValueTypeSystem.Object"来保持打开/关闭的假装,还是应该base.ToString()在返回"1"之前将调用分配给未使用的变量?

显然没有这些.它应尽可能返回有意义的字符串.它的基类型尽可能地返回有意义的字符串,并且扩展它不会受益于对其基数的调用.

打开/关闭原则意味着当我调用Foo()时,我希望发生一些Fooing,并且当我在Level1上调用它时我期望一些Level1适当的Fooing,而当我在Level2上调用它时,我期望一些Level2适当的Fooing.Level2 Fooing是否应该涉及一些Level1 Fooing也会发生,取决于Fooing是什么.

base 是一个工具,可以帮助我们扩展类,而不是一个要求.