【问题标题】:can overriding of a method be prevented by downcasting to a superclass?可以通过向下转换到超类来防止方法的覆盖吗?
【发布时间】:2012-01-16 20:56:35
【问题描述】:

我试图了解以下问题的答案是否在所有主要的 OOP 语言中都是相同的;如果不是,那么这些语言有何不同。

假设我有定义方法actjump 的类A;方法act 调用方法jumpA 的子类 B 覆盖方法 jump(即,使用适当的语法来确保无论何时调用 jump,都会使用类 B 中的实现)。

我有 bB 的对象。我希望它的行为与 A 类完全一样。换句话说,我希望使用A 中的实现来执行jump。我有哪些不同语言的选择?

例如,我可以通过某种形式的向下转换来实现这一点吗?或者也许通过创建一个知道要调用哪些方法的代理对象?

我想避免创建一个全新的 A 类对象,并仔细设置 ab 之间的内部状态共享,因为这显然不是面向未来的,而且很复杂。我还想避免将b 的状态复制到A 类的全新对象中,因为可能需要复制大量数据。

更新

我问了这个问题specifically about Python但这似乎在 Python 中是不可能实现的,从技术上讲,它可以做到......有点......

似乎除了技术上的可行性之外,从设计的角度来看,反对这样做的理由也很强烈。我在a separate question 询问这个问题。

【问题讨论】:

  • 确实——在 Python 中甚至不存在强制转换的概念。有可能破解大量的内省以使其工作 - 但我认为在其他动态语言中它也不应该是可能的。
  • 委派 act 的实现并跳转到其他类(也许是策略),然后不要理会 B。通常,如果您现有的设计让您寻找规避良好 OO 设计的方法,它是是时候退后一步问问原因了。
  • @TerryWilcox:但是这个问题不会出现在动态绑定的任何继承中吗?在这种情况下,每当出现此问题时,我(可能)必须准备好将任何代码从继承重构为其他方法。这将使使用多态性的前景变得非常没有吸引力。我错过了什么?
  • 子类化就是改变行为。如果您希望您的实例 b 表现得好像它是一个 A,那么它首先不应该是一个 B。如果我处于您的情况,我会接受继承是一个糟糕的设计并转向组合。例如,只有 A,它使用策略模式来实现不同的行为。
  • @TerryWilcox:那么你会说不太可能需要抑制多态性吗?但如果在我的应用程序中我认为这是一种明显的可能性,我应该尝试使用策略模式,它更强大,但编码成本略高?

标签: oop class inheritance overriding method-hiding


【解决方案1】:

cmets 重申:更喜欢组合而不是继承。

当您的子类与其超类有明确定义的行为差异时,继承效果很好,但您经常会遇到模型变得笨拙或失去意义的地步。此时,您需要重新考虑您的设计。

组合通常是更好的解决方案。将对象的不同行为委托给不同的对象(或多个对象)可能会减少或消除您对子类化的需求。

在您的情况下,A 类和 B 类之间的行为差​​异可以封装在策略模式中。然后,您可以在实例级别更改 A 类(和 B 类,如果仍然需要)的行为,只需分配一个新策略即可。

策略模式在短期内可能需要更多代码,但它干净且可维护。方法调配、猴子补丁以及所有允许我们在特定语言实现中四处探索的很酷的东西都很有趣,但出现意外副作用的可能性很高,而且代码往往难以维护。

【讨论】:

    【解决方案2】:

    您所问的内容与 OOP 编程完全无关/不受支持。

    如果您使用类 B 子类化对象 A 并覆盖其方法,则当创建 B 的具体实例时,所有基方法的覆盖/新实现都与它相关联(我们讨论关于带有虚拟表的 Java 或 C++ 等)。

    你已经实例化了对象B
    如果你已经重写了超类的方法,你为什么期望你可以/将/应该能够调用超类的方法?

    您当然可以明确地调用它,例如通过在方法内调用super,但您不能自动执行此操作,并且强制转换也不会帮助您执行此操作。

    我无法想象你为什么要这样做。
    如果您需要使用A 类,请使用A 类。
    如果您需要覆盖其功能,请使用其子类B

    【讨论】:

    • 我不能告诉你为什么像 C++ 这样的语言支持非虚拟方法,但它确实支持。
    • virtual 在 C++ 中用于声明一个函数可以被覆盖。一旦函数被声明为virtual,那么子类的方法就会在运行时运行。即多态性不像在 Java 中那样默认运行。但这与OP的问题无关
    • @user384706:是的,我的问题是,一旦在类定义中启用了覆盖(在 C++ 的情况下,使用 virtual 关键字),是否可以(并且应该)做某事。
    【解决方案3】:

    大多数编程语言在支持虚函数的动态调度方面遇到了一些麻烦(在子类而不是父类的实现中调用重写方法 jump 的情况)——在某种程度上解决它或避免它很难。一般来说,专业化/多态性是一个理想的特性——可以说是 OOP 的首要目标。

    查看Virtual Functions 上的 Wikipedia 文章,该文章对许多编程语言中对虚函数的支持进行了有用的概述。在考虑特定语言时,它将为您提供一个起点,以及在查看程序员可以控制调度行为方式的语言时权衡权衡(例如,参见 C++ 部分)。

    简单地说,您的问题的答案是:“不,所有编程语言的行为都不相同。”此外,没有独立于语言的解决方案。如果您需要该行为,C++ 可能是您的最佳选择。

    【讨论】:

    • 没有virtual polyphormism 在 C++ 中不起作用。这不是 OP 所要求的。与子类未定义方法相同
    • 听起来就像 OP 要求进行非虚拟方法调用(如 C++)。
    • 您没有抓住重点。如果该方法是非虚拟的,则与从不被覆盖一样。 IE。仅调用 A 类方法。从不属于 B 类。OP 想要调用 B 类的方法,有时如果他愿意,也可以调用 A 类的方法。
    • 我很抱歉造成混乱。 @user384706:你是对的,我不想修改类定义。即使我可以在 A 类中用非虚拟方法替换 virtual,它也不起作用,因为当 B 类开始表现得像 A 类时,许多其他 B 类用户作为 A 类对象传递时会感到非常惊讶。跨度>
    • @max:所以你有一个用例?这不是一个理论问题?你到底想做什么?
    【解决方案4】:

    你实际上可以用 Python 来做这件事(有点),有一些可怕的 hack。它要求您实现类似于我们在您的第一个 Python 特定问题中讨论的包装器,但 作为 B 的子类。然后您还需要实现写代理(包装器对象不应包含任何通常与类层次结构关联的状态,它应将所有属性访问重定向到 B 的底层实例。

    但不是将方法查找重定向到 A,然后使用包装的实例调用该方法,而是调用将包装器对象作为self 传递的方法。这是合法的,因为包装器类是 B 的子类,因此包装器实例是您正在调用其方法的类的实例。

    这将是非常奇怪的代码,需要您同时使用 IS-A 和 HAS-A 关系动态生成类。它也可能最终变得相当脆弱,并在很多极端情况下产生奇怪的结果(你通常不能完全用 Python 编写 100% 完美的包装类,因为这种奇怪的事情是可能的)。

    我完全不考虑天气这是否是个好主意。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-07
      • 2015-10-27
      • 2017-08-27
      • 2010-11-10
      • 1970-01-01
      • 2014-01-30
      相关资源
      最近更新 更多