Oop 在没有RTTI或基类修改的情况下对基类或子类进行操作

Oop 在没有RTTI或基类修改的情况下对基类或子类进行操作,oop,Oop,我问了一个特定于某项技术的问题,但现在我发现自己对广义的主题感到疑惑 为了简单起见,我们有两个类,A和B,其中B是从A派生的。B实际上“是”A,并且A中定义的所有例程在B中具有相同的含义 假设我们想显示一个As列表,其中一些实际上是Bs。当我们遍历As列表时,如果当前对象实际上是a B,我们想显示B的一些附加属性……或者我们只是想给B添加不同的颜色,但a和B都没有“颜色”或“显示内容”的概念 解决方案: 通过在A中包含一个名为isB()的方法(该方法返回false),使A类半了解B。B将重写该方

我问了一个特定于某项技术的问题,但现在我发现自己对广义的主题感到疑惑

为了简单起见,我们有两个类,A和B,其中B是从A派生的。B实际上“是”A,并且A中定义的所有例程在B中具有相同的含义

假设我们想显示一个As列表,其中一些实际上是Bs。当我们遍历As列表时,如果当前对象实际上是a B,我们想显示B的一些附加属性……或者我们只是想给B添加不同的颜色,但a和B都没有“颜色”或“显示内容”的概念

解决方案:

  • 通过在A中包含一个名为isB()的方法(该方法返回false),使A类半了解B。B将重写该方法并返回true。显示代码将有如下检查:if(currentA.isB())B=currentA

  • 在a中提供一个B可以重写的display()方法。。。。然后我们开始合并UI和模型。除非有一些我看不到的酷把戏,否则我不会考虑这个问题。

  • 使用instanceof检查要显示的当前A对象是否真的是B

  • 只需将B中的所有垃圾添加到A中,即使它不适用于A。基本上,只需在A中包含B(不从A继承),并将其设置为null,直到应用为止。这有点吸引人。这类似于#1,我想w/组合优于继承

  • 似乎这个特殊的问题应该不时出现,并有一个明显的解决办法

    所以我想问题可能真的可以归结为:


    如果我有一个子类通过添加附加功能(不仅仅是更改基类的现有行为)来扩展基类,我是不是做错了什么悲剧?当我们试图对可能是a或B的对象集合采取行动时,一切似乎都会立即崩溃。

    这看起来像是访问者设计模式的教科书案例(也称为“双重分派”)

    有关访问者和复合模式的详细说明,请参见链接。

    选项2的变体(或1和2的混合)可能有意义:毕竟,多态性是“B是a,但在情况X下需要不同的行为”的标准解决方案。同意,display()方法可能会将模型与UI联系得太紧密,但是,您希望在UI级别使用的不同呈现可能反映了模型级别的语义或行为差异。这些可以用一种方法捕捉吗?例如,它可能不是一个直接的getDisplayColor()方法,而是一个getPriority()(例如)方法,其中a和B返回不同的值,但如何将其转换为颜色仍然取决于UI

    然而,考虑到您更一般的问题“我们如何处理我们不能或不允许通过基类进行多态访问的其他行为”,例如,如果基类不在我们的控制之下,您的选项可能是选项3,访问者模式或助手类。在这两种情况下,您都有效地将多态性分配给外部实体——在选项3中,UI(例如,演示者或控制器),它执行instanceOf检查,并根据是否为a B执行不同的操作;在Visitor或helper情况下,新类。以您的示例为例,Visitor可能有些过火(另外,如果您不能/不愿意更改基类以适应它,我认为不可能实现它),因此我建议使用一个简单的类,称为“renderer”:

    这将封装运行时类型检查,并将代码捆绑到定义合理、职责明确的类中,而不需要访问者的概念性开销。(不过,根据GrizzlyNyo的回答,如果您的层次结构或函数集比这里显示的更复杂,那么Visitor可能更合适,但许多人发现Visitor很难理解,我倾向于在简单的情况下避免使用它——但您的里程可能会有所不同。)作者给出的答案涵盖了问题的大部分内容。我现在将尽可能简单地讨论最后一段

    继承的实现应该是为了重用,为了在旧代码中重用派生类,而不是为了重用基类的一部分(可以使用聚合)

    从这个角度来看,如果您有一个类要用于具有某些新功能的新代码,但应该作为以前的类透明地使用,那么继承就是您的解决方案。新代码可以使用新功能,旧代码将无缝地使用新对象

    虽然这是总体意图,但也有一些常见的陷阱,这里的线很微妙,你的问题正是关于这条线。如果您有一个base类型的对象集合,那应该是因为这些对象只用于base的方法。他们是“基地”,表现得像基地

    使用“instanceof”或downcast(C++中的dynamic_cast())等技术来检测实际的运行时类型,我会在代码审查中标记,只有在让程序员详细解释为什么其他选项比该解决方案更糟糕后,我才会接受。例如,我会接受他在以下前提下的回答,即在基地的给定操作中无法获得信息。也就是说,基类型没有任何方法可以为调用方提供足够的信息来确定颜色。如果包含这样的操作是没有意义的:除了预呈现颜色之外,您是否打算基于相同的信息对对象执行任何操作?如果逻辑依赖于实数类型,那么操作应该在基类中,以便在派生类中重写。如果不可能(操作是新的,仅适用于某些给定的子类型),则应
    public abstract class Renderer {
      public static Renderer Create(A obj) {
        if (obj instanceOf B)
          return new BRenderer();
        else
          return new ARenderer();
      }
    
      public abstract Color getColor();
    }
    
    // implementations of ARenderer and BRenderer per your UI logic