7

假设我有以下人为的代码:

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 所说的那样,而且完全是上下文相关的;但在我接受任何答案之前,我将让讨论开放一段时间。

4

3 回答 3

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是帮助我们扩展类的工具,而不是要求。

于 2010-10-07T17:11:23.163 回答
3

无法仅通过静态查看它来确定系统(或类)是否遵循 OCP,没有上下文信息。

只有知道设计的可能更改是什么,您才能判断它是否遵循 OCP 对于这些特定类型的更改。

没有任何语言结构可以帮助您。

一个好的启发式方法是使用某种度量,例如 Robert Martin 的不稳定性和抽象性 (pdf),或缺乏凝聚力的度量,以更好地为您的系统做好准备,以更好地遵循 OCP 和所有其他OOD 的重要原则。

于 2010-10-08T14:07:08.650 回答
1

OCP 是关于前置条件和后置条件的:“当你只能用一个较弱的前置条件替换它的前置条件,而用一个更强的前置条件替换它的后置条件时”。我不认为在重写方法中调用 base 会违反它。

于 2011-12-03T23:42:05.653 回答