热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

Iaskedasimilarquestionyesterdaythatwasspecifictoatechnology,butnowIfindmyselfwonder

I asked a similar question yesterday that was specific to a technology, but now I find myself wondering about the topic in the broad sense.

昨天我问了一个类似于技术的问题,但现在我发现自己对广义上的这个话题感到疑惑。

For simplicity's sake, we have two classes, A and B, where B is derived from A. B truly "is a" A, and all of the routines defined in A have the same meaning in B.

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

Let's say we want to display a list of As, some of which are actually Bs. As we traverse our list of As, if the current object is actually a B, we want to display some of Bs additional properties....or maybe we just want to color the Bs differently, but neither A nor B have any notion of "color" or "display stuff".

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

Solutions:

  1. Make the A class semi-aware of B by basically including a method called isB() in A that returns false. B will override the method and return true. Display code would have a check like: if (currentA.isB()) B b = currentA;

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

  2. Provide a display() method in A that B can override.... but then we start merging the UI and the model. I won't consider this unless there is some cool trick I'm not seeing.

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

  3. Use instanceof to check if the current A object to be displayed is really a B.

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

  4. Just add all the junk from B to A, even though it doesn't apply to A. Basically just contain a B (that does not inherit from A) in A and set it to null until it applies. This is somewhat attractive. This is similar to #1 I guess w/ composition over inheritance.

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

It seems like this particular problem should come up from time to time and have an obvious solution.

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

So I guess the question maybe really boils down to:

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

If I have a subclass that extends a base class by adding additional functionality (not just changing the existing behavior of the base class), am I doing something tragically wrong? It all seems to instantly fall apart as soon as we try to act on a collection of objects that may be A or B.

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

3 个解决方案

#1


A variant of option 2 (or hybrid of 1 and 2) may make sense: after all, polymorphism is the standard solution to "Bs are As but need to behave differently in situation X." Agreed, a display() method would probably tie the model to the UI too closely, but presumably the different renderings you want at the UI level reflect semantic or behavioural differences at the model level. Could those be captured in a method? For example, instead of an outright getDisplayColour() method, could it be a getPriority() (for example) method, to which A and B return different values but it is still up to the UI to decide how to translate that into a colour?

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

Given your more general question, however, of "how can we handle additional behaviour that we can't or won't allow to be accessed polymorphically via the base class," for example if the base class isn't under our control, your options are probably option 3, the Visitor pattern or a helper class. In both cases you are effectively farming out the polymorphism to an external entity -- in option 3, the UI (e.g. the presenter or controller), which performs an instanceOf check and does different things depending on whether it's a B or not; in Visitor or the helper case, the new class. Given your example, Visitor is probably overkill (also, if you were not able/willing to change the base class to accommodate it, it wouldn't be possible to implement it I think), so I'd suggest a simple class called something like "renderer":

然而,考虑到你的一般性问题,“我们如何处理我们不能或不允许通过基类以多态方式访问的其他行为”,例如,如果基类不在我们的控制之下,那么选项可能是选项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

This encapsulates the run-time type checking and bundles the code up into reasonably well-defined classes with clear responsibilities, without the conceptual overhead of Visitor. (Per GrizzlyNyo's answer, though, if your hierarchy or function set is more complex than what you've shown here, Visitor could well be more appropriate, but many people find Visitor hard to get their heads around and I would tend to avoid it for simple situations -- but your mileage may vary.)

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

#2


The answer given by itowlson covers pretty well most part of the question. I will now deal with the very last paragraph as simply as I can.

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

Inheritance should be implemented for reuse, for your derived class to be reused in old code, not for your class reusing parts of the base class (you can use aggregation for that).

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

From that standpoint, if you have a class that is to be used on new code with some new functionality, but should be used transparently as a former class, then inheritance is your solution. New code can use the new functionality and old code will seamlessly use your new objects.

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

While this is the general intention, there are some common pitfals, the line here is subtle and your question is about precisely that line. If you have a collection of objects of type base, that should be because those objects are meant to be used only with base's methods. They are 'bases', behave like bases.

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

Using techniques as 'instanceof' or downcasts (dynamic_cast<>() in C++) to detect the real runtime type is something that I would flag in a code review and only accept after having the programmer explain to great detail why any other option is worse than that solution. I would accept it, for example, in itowlson's answer under the premises that the information is not available with the given operations in base. That is, the base type does not have any method that would offer enough information for the caller to determine the color. And if it does not make sense to include such operation: besides the prepresentation color, are you going to perform any operation on the objects based on that same information? If logic depends on the real type, then the operation should be in base class to be overriden in derived classes. If that is not possible (the operation is new and only for some given subtypes) there should at least be an operation in the base to allow the caller to determine that a downcast will not fail. And then again, I would really require a sound reason for the caller code to require knowledge of the real type. Why does the user want to see it in different colors? Will the user perform different operations on each one of the types?

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

If you endup requiring to use code to bypass the type system, your design has a strange smell to it. Of course, never say never, but you can surely say: avoid depending on instanceof or downcasts for logic.

如果您最终需要使用代码来绕过类型系统,那么您的设计会有一种奇怪的气味。当然,永远不要说永远,但你可以肯定地说:避免依赖于instanceof或downcasts的逻辑。

#3


This looks like text book case for the Visitor design pattern (also known as "Double Dispatch").

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

See this answer for link to a thorough explanation on the Visitor and Composite patterns.

有关访问者和复合模式的详细说明,请参阅此答案。


推荐阅读
author-avatar
曹彩节
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有