tags:

views:

117

answers:

3

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.

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".

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;

  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.

  3. Use instanceof to check if the current A object to be displayed is really 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.

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: 

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.

Gabi Davar
+2  A: 

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?

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":

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.)

itowlson
+1 on the second paragraph. It is the external code that needs to differentiate for a reason that the two classes do not care. One class one responsibility, and your class (hierarchy) responsibility is not selection of the color to be used in the window.
David Rodríguez - dribeas
+1  A: 

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.

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 funtionality, 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.

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?

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.

David Rodríguez - dribeas
In very simple cases, what you said ("new functions use new objects, happy!") is true. Now consider this very real-world scenario: You implement new subclass `X` which inherits old base class `A`. You feed a container of mixed `X` and `A` to `OldMethod()` which can't operate on `X`, but you want the results to be fed to `NewMethod()` which recognizes and uses both `X` and `A`. `NewMethod()` needs to somehow differentiate `X` from the `A`.
kizzx2
I guess this is an unsolved problem (yet) -- many widespread languages don't support mulit-method (the ones that I know of are dynamic languages -- they use RTTI for probably a simple addition/subtraction); GOF's Visitor pattern is downright ugly (encapsulation is non-existant in the visitor side); Uncle Bob's Acyclic visitor had to finally resort to downcasting -- not to mention the number of classes needed to support the idiom will probably outnumber the concrete classes pretty fast.
kizzx2
(Not to say that what you said is wrong. Just to point out that it's probably needed more often than you might have sounded :)
kizzx2