【问题标题】:When inheriting from a class hurts you later?当从一个类继承以后会伤害你?
【发布时间】:2010-09-05 15:55:45
【问题描述】:

您能否提供当我们从一个类继承时的场景,它工作了一段时间,但随后发生了其他变化并引入了一个错误? 我想出了以下情况:

  • 为了实现带孔的矩形,我们从类 Rectangle 继承。在构造函数中,我们检查孔是否在矩形内
  • 后来有人为类 Rectangle 添加了一个新方法 Resize。他们不检查洞是否还在里面。
  • 调整大小后,我们可以在矩形内部有孔的矩形孔,这是一个错误。

如果我选择在 C# 中使用对象继承,我应该注意哪些其他问题。

【问题讨论】:

  • 这不是在 C# 中要小心的事情,在任何面向对象的语言中都是要小心的。基类应该永远不必担心它们的后代。
  • @Robaticus:我说 C# 是因为在 C++ 中我们可以从多个类继承,并且更容易陷入困境。在 C# 中,我们只能从一个类继承。
  • 我仍然坚持我所说的。无论使用哪种语言,都需要注意这一点。它不是 C# 独有的。

标签: c# inheritance oop


【解决方案1】:

您所描述的是继承的陷阱之一。

另一个陷阱是深度继承层次结构。 composition over inheritance 上的 Stackoverflow 线程。

【讨论】:

    【解决方案2】:

    对基类的更改可以通过多种方式影响派生类的行为。如果作者突然决定以sealed为例呢?

    就像任何接口一样,如果消费者不需要修改,它就需要稳定。

    继承的主要“规则”是里氏替换原则。这表明派生类应该可以替代基类或从它派生的其他类。

    这个案例从表面上打破了这个规则,因为有孔的矩形不是矩形。

    通常最好使用接口将行为划分为可实现的合理块。这种接口通常用形容词而不是名词命名。例如,对于要渲染的矩形和带孔的矩形都是有意义的,因此接口可能是IRenderable。如果它是可调整大小的,您可能有一个 IResizable 等。这些可以聚合成一个 IShape,但您要小心,您对 Shape 的定义只定义了您的问题域中的行为。

    直接从另一个类派生可能很危险,因为您会受到该类行为的约束。如果您确实需要这样做,最好将您需要的实现提取到一个公共基类中(例如Rectangle : RectangleImplementation, IShape)。

    【讨论】:

      【解决方案3】:

      这被称为brittle base class problem

      一般来说,另一个潜在的继承问题是当您想使用自己的基类但框架需要特定的基类(例如,ContextBoundObject)而不是接口时。

      【讨论】:

        【解决方案4】:

        这就是有先见之明的 C# 语言设计者在语言中添加 sealed 关键字的原因。谨慎使用。

        并使用代码契约来测试您的不变量,以获得有关损坏的早期警告。

        【讨论】:

        • 汉斯,你所说的“使用代码契约来测试你的不变量以获得关于破损的早期警告”是什么意思?
        【解决方案5】:

        不要在构造函数中调用虚方法。请参阅 Eric Lippert 的相关帖子(part 1part 2)。

        【讨论】:

          【解决方案6】:

          我认为这与 c# 或继承无关。不管你做什么,这都是软件开发不可避免的问题。

          最好和简单的解决方案是每天构建您的代码并执行unit testing

          【讨论】:

          • 我们总是使用单元测试和 CI,但我看不出在这种情况下有什么帮助。我们不会针对尚不存在的情况创建单元测试。在开发之前,我们不可能有一个单元测试来预测 Resize 方法。你能建议它是如何发生的吗?
          • 单元测试通常调用方法并比较输入和输出。我正在针对类似问题进行基于场景的单元测试。以你的例子;创建矩形后,对象传递给 Rectangle 测试(执行调整大小、移动等)测试并通过相同上下文的 Rectangle-With-Hole 测试(执行边界检查)。它完全取决于开发人员的愿景,并不完美,但有时比方法测试效果更好。顺便说一句,它并没有取代方法测试,它只是一个额外的测试。
          【解决方案7】:

          在这些类型的情况下最好使用 -Decorator 模式。 这提供了更好的扩展方式。

          这可以使用下图来实现:

          矩形对象将被创建并包装到一个负责提供孔的 HoleDecorator 对象中。调整 Rectangle 的大小后,会调用 HoleDecorator 的 Resizing,这将首先调用 Rectangle 对象的 Resize,然后调用 Hole Decorator 的 AdditionalBehavior,它会指定当主组件被替换时要对孔做什么调整大小。

          【讨论】:

          • 能否指定您的 Rectangle-with-hole 实现是什么样的?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-01-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-11-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多