抽象方法和开放原理

时间:2010-10-07 16:42:06

标签: 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();
  }
}

如果我只查看Level2类,我可以立即看到Level2.PrintHierarchy跟在the open/closed principle之后,因为它自己做了一些事情并调用了基础方法,它覆盖了Level1

但是,如果我只查看base.PrintHierarchy类,它似乎违反了OCP,因为它没有调用Level1 - 实际上,在C#中,编译器禁止它错误“无法调用抽象基础成员”。

使Root.PrintHierarchy看起来遵循OCP的唯一方法是将PrintHierarchy更改为空的虚方法,但是我不能再依赖编译器来强制派生类来实现{{1 }}

我在维护代码时遇到的真正问题是看到几十个不调用override的{​​{1}}方法。如果base.Whatever()是抽象的,那么很好,但如果没有,那么base.Whatever方法可能是被拉入接口的候选者而不是具体的可覆盖方法 - 或类或方法需要以某种方式进行重构,但不管怎样,它都清楚地表明设计不佳。

如果没有记住Whatever是抽象的或在Root.PrintHierarchy内添加评论,我是否还有其他选项可以快速确定类似Level1.PrintHierarchy的类是否违反了OCP?


评论中有很多很好的讨论,也有一些很好的答案。我无法弄清楚到底要问什么。我觉得让我感到沮丧的是,as @Jon Hanna points out,有时虚拟方法只是表示“你必须实现我”,而有时候它意味着“你必须扩展我 - 如果你没有调用基础版本,你就会打破我的设计!”但是C#没有提供任何方式来表明你的意思,除了那个抽象或界面显然是一个“必须实现”的情况。 (除非代码合同中有某些内容,我认为这有点超出范围)。

但是如果一个语言 有一个必须实现与必须扩展的装饰器,如果它不能被禁用,它可能会为单元测试带来巨大的问题。有这样的语言吗?这听起来很像design-by-contract,所以如果它出现在埃菲尔,我也不会感到惊讶。

最终结果可能是as @Jordão says,而且完全是上下文的;但在我接受任何答案之前,我将暂时搁置讨论。

3 个答案:

答案 0 :(得分:5)

Root定义以下内容: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是一种帮助我们扩展课程的工具,而不是一项要求。

答案 1 :(得分:3)

无法决定系统(或类)是否通过静态查看系统(或类),而没有上下文信息。

只有当知道 可能对设计的更改时,才能判断它是否遵循OCP进行这些特定类型的更改。

没有可以帮助你的语言结构。

一个好的启发式方法是使用某种指标,例如罗伯特·马丁的instability and abstractness (pdf),或指导缺乏凝聚力的指标,以更好地为更改系统做好准备,并拥有更好的遵循OCP的机会以及OOD的所有其他重要原则。

答案 2 :(得分:1)

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