views:

118

answers:

5

I'm trying to solve a design issue using inheritance based polymorphism and dynamic binding. I have an abstract superclass and two subclasses. The superclass contains common behaviour. SubClassA and SubClassB define some different methods: SubClassA defines a method performTransform(), but SubClassB does not.

So the following example

1 var v:SuperClass;

2 var b:SubClassB = new SubClassB();

3 v = b;

4 v.performTransform();

would cause a compile error on line 4 as performTransform() is not defined in the superclass. We can get it to compile by casting...

(v as SubClassA).performTransform();

however, this will cause a runtime exception to be thrown as v is actually an instance of SubClassB, which also does not define performTransform()

So we can get around that by testing the type of an object before casting it:

if( typeof v == SubClassA)
{
  (cast v to SubClassA).performTransform();
}

That will ensure that we only call performTransform() on v's that are instances of SubClassA. That's a pretty inelegant solution to my eyes, but at least its safe. I have used interface based polymorphism (interface meaning a type that can't be instantiated and defines the API of classes that implement it) in the past, but that also feels clunky. For the above case, if SubClassA and SubClassB implemented ISuperClass that defined performTransform, then they would both have to implement performTransform(). If SubClassB had no real need for a performTransform() you would have to implement an empty function.

There must be a design pattern out there that addresses the issue.

+4  A: 

My immediate comment is that your object modelling is wrong. Why treat SubClassA as a SuperClass (is-a relationship), when I would suggest that it's not.

You could implement a dummy performTransform() that does absolutely nothing in its base instance, and is overridden in SubClassA. But I'm still concerned that on one hand you're treating all these objects (SubClassA, SubClassB) as the same thing, and then wanting to treat them differently depending on their real implementation, rather than the interface they present.

Brian Agnew
Why not? We don't know what else is involved with the implementation of the two classes. As far as I can tell, a `SubClassA` is a `SuperClass` that can `performTransform`.
danben
His requirements make it clear that it's not.
Brian Agnew
Well with an OOP language its usually possible to add behaviour to subclasses that don't exist in the superclass, making them more specialised. IMO that still an is-a relationship. But I know where you're coming from.
Graham Phillips
Sure, you can add behavior to subclasses, but typically if you want the subclass to strongly conform to the interface of the superclass (which is what the OP seems to want here) you're not going to add public interfaces.
pkh
Usually, when I find myself in this trap (having a superclass pointer but wanting to call a possibly-missing method on it), I just define a do-nothing or throw-exception version in the superclass (that gets overridden in the correct subclasses) and get on with life.
Mike DeSimone
+3  A: 

Assuming you are using a strongly-typed language, which your question seems to indicate...

There is no design pattern to work around this, because this is the intended behavior.

In your definition, performTransform belongs only to SubClassA. Thus, to be able to invoke performTransform on an object, the object must be of type SubClassA (or a subtype of SubClassA.

Invoking performTransform on a SuperClass does not make sense because not every instance of SuperClass defines this method.

Downcasting from a SuperClass to a SubClassA should certainly throw an error if the instance is not a SubClassA - this should be obvious.

So, you must either change your definitions such that performTransform belongs to SuperClass (in which case, as you said, every instance of type SuperClass would need to have some implementation for the method, even an empty one) or you must make sure that you are only invoking methods on types that define them.

danben
This is what I thought was the case, but I wondered if the OOP gurus out there had come up with something better since I studied this stuff at school 15 years ago.
Graham Phillips
My point is that it would not be "better" to allow you to call this method on any object you want because some will be known not to implement it. For your purposes, you may want to look into a dynamically-typed language like Python, where a method invocation will result in the instance being checked at runtime for an implementation of the method.
danben
Objective C allows this sort of thing, but only because default behavior is to "do nothing, return nothing" if the message handler is unimplemented. FWIW, any time I've seen someone try to solve the posted problem (lose the type information then try to get it back), it's been an indication of a larger design problem. If you need the type information, keep it.
dash-tom-bang
@danben yes, I'm using a dynamically typed language, but the behaviour is for a runtime exception where there is no implemented method.
Graham Phillips
@Graham then you just need to check for the existence of that method. In Python you might just wrap it in a `try: obj.MyMethod(); except AttributeError: pass` handler, but consider your class hierarchy carefully and try to come up with a way to architect it so that you don't need to guess at what methods the object supports.
dash-tom-bang
+1  A: 

I'm not so sure it requires a pattern to solve but instead just a small redesign. If it makes sense for anything to call performTransform is should be in the superclass as a virtual method and overridden in the subclasses.

So the superclass defines the flow from an abstract viewpoint and the subclasses implement them appropriately. In your case, the simplest options are to either just leave performTransform empty in the superclass or implement it as an empty method in the subclass that doesn't require it (when you mix this approach with a short comment, you get a more maintainable system IMO).

The closest pattern I can think of for this is the Null Object pattern where this performTransform method is just a dummy function to preserve compatibility but perform no actual task.

Austin Salonen
A: 

It Would be better to have the call to performTransform() in a method that only takes type SubClassB as a parameter - at least you wouldn't have to do type checking then.

On saying that, if your having this problem at all it may suggest that inheritance may not be the best solution - composition may be a better way to approach the problem.

David Relihan
I'm always on the lookout for opportunities to use composition over inheritance, but its not always the right solution.
Graham Phillips
+1  A: 

Just because you say your bicycle is a car doesn't mean there's a place to put gas in it. The whole point of polymorphism is to let you think of things as the super class - these are all bank accounts, these are all shapes, to use the classic examples - and not get caught up in what they really are. Sometimes the subclasses add capability. In many cases that capability is used in the specific implementations in each subclass. So to use your names, some method Adjust() that is in the signature of SuperClass is implemented (differently) in SubClassA and SubClassB. The SubClassA version calls its own performTransform as part of the process and we all live happily ever after. The minute some code needs to decide whether to call performTransform or not, you're not just thinking of it as a SuperClass any more. That's not necessarily something that needs to be solved, it's just what is.

Kate Gregory