在没有RTTI或基类修改的基础或子类上行动

时间:2009-04-17 21:04:48

标签: oop

我问了一个专门针对某项技术的similar question yesterday,但现在我发现自己对广义上的这个话题感到疑惑。

为简单起见,我们有两个类,A和B,其中B来自A. B真正“是”A,并且A中定义的所有例程在B中具有相同的含义。

假设我们要显示As的列表,其中一些实际上是B。当我们遍历我们的As列表时,如果当前对象实际上是B,我们想要显示一些Bs的附加属性....或者我们只想以不同的方式为Bs着色,但A和B都没有任何概念“颜色”或“显示东西”。

解决方案:

  1. 通过在A中包含一个名为isB()的方法使A类半知晓B,返回false。 B将覆盖该方法并返回true。显示代码的检查如下:if(currentA.isB())B b = currentA;

  2. 在A中提供一个B可以覆盖的display()方法....但是我们开始合并UI和模型。除非有一些我没有看到的很酷的技巧,否则我不会考虑这个。

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

  4. 只需将B中的所有垃圾添加到A,即使它不适用于A.基本上只包含A中的B(不从A继承)并将其设置为null,直到它适用。这有点吸引人。这与#1类似,我猜是遗传的构成。

  5. 似乎这个特殊问题应该不时出现,并有一个明显的解决方案。

    所以我想这个问题可能归结为:

    如果我有一个通过添加额外功能扩展基类的子类(不仅仅是改变基类的现有行为),我是否在做一些悲惨的错误?一旦我们尝试对可能是A或B的物体集合采取行动,这一切似乎立刻就会崩溃。

3 个答案:

答案 0 :(得分:2)

选项2的变体(或1和2的混合)可能有意义:毕竟,多态性是“Bs为As但在情况X中需要表现不同”的标准解决方案。同意,display()方法可能会将模型与UI紧密联系在一起,但可能在UI级别所需的不同渲染反映了模型级别的语义或行为差异。那些可以用方法捕获吗?例如,不是一个彻头彻尾的getDisplayColour()方法,它可能是一个getPriority()(例如)方法,A和B返回不同的值,但仍然由UI决定如何将其转换为颜色?

然而,鉴于您提供的更为一般的问题,“我们如何处理其他行为,我们不能或不允许通过基类以多态方式访问”,例如,如果基类不在我们的控制之下,你的选项可能是选项3,访问者模式或帮助类。在这两种情况下,你都有效地将多态性转化为外部实体 - 在选项3中,UI(例如演示者或控制器)执行instanceOf检查并根据它是否为B来执行不同的操作;在Visitor或helper case中,新类。举个例子,访问者可能是矫枉过正(如果你不能/不愿意改变基类以适应它,我认为不可能实现它),所以我建议一个叫做某事的简单类比如“渲染器”:

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

这封装了运行时类型检查,并将代码捆绑到具有明确职责的合理定义的类中,而没有访问者的概念开销。 (但是,根据GrizzlyNyo的回答,如果您的层次结构或功能集比您在此处显示的更复杂,访客可能更合适,但很多人发现访客很难理解,我倾向于避免它简单的情况 - 但你的里程可能会有所不同。)

答案 1 :(得分:1)

itowlson给出的答案涵盖了问题的大部分内容。我现在将尽可能简单地处理最后一段。

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

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

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

使用技术作为'instanceof'或向下转换(C ++中的dynamic_cast<>())来检测真实的运行时类型是我在代码审查中标记的东西,只有在让程序员详细解释为什么其他选择比解决方案更糟糕。我会接受它,例如,在itowlson的答案中,在该基础上,给定的操作无法获得信息。也就是说,基本类型没有任何方法可以为调用者提供足够的信息来确定颜色。如果包含这样的操作没有意义:除了预表示颜色之外,您是否要根据相同的信息对对象执行任何操作?如果逻辑依赖于实际类型,则操作应该在基类中以在派生类中重写。如果这是不可能的(操作是新的并且仅针对某些给定的子类型),则至少应该在基础中进行操作以允许调用者确定向下转换不会失败。然后,我真的需要一个合理的理由让调用者代码需要了解真实类型。为什么用户想要以不同的颜色看到它?用户是否会对每种类型执行不同的操作?

如果您最终要求使用代码绕过类型系统,那么您的设计会有一个奇怪的smell。当然,永远不要说永远,但你可以肯定地说:避免依赖于 instanceof 或向下倾斜逻辑。

答案 2 :(得分:0)

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

有关访问者和复合模式的详细说明,请参阅this answer