views:

481

answers:

9

I think of this as broken OOP. It's starting to grow like mold in our code base and I need to start talking with people about cleaning it up. Is there a common name for it?

public void eat(Fruit fruit) {

    if (fruit instanceof Banana) {
        ((Banana) fruit).peel();
    }
    else if (fruit instanceof Pineapple) {
        ((Pineapple) fruit).slice();
    }

    // ... etc ...

    else if (fruit instanceof Strawberry) {
        // n/a ;-)
    }

    fruit.eat();
}
+7  A: 

It's called a "type switch", like a switch statement on the type of an object.

Another question that will probably be of interest to you.

Daniel Earwicker
Ahh, Skeet to the rescue! Thanks for the link, I will have to decipher the C# stuff but I think that might be slightly relevant in our case. While we do control the types involved, I think we are lacking a layer... the types involved are persistent objects (Hibernate entities) and may not be appropriate to contain higher level logic like that.
Mark Renouf
Actually many people argue that persistent objects should have whatever logic they need to fit your needs; there's no good reason to keep the persistent objects "dumb".
Daniel Earwicker
Good point. I share your view... though I see some benefits in more loosely coupling the two. It's quick and easy but suddenly there's lots of friction to change if there's stuff in the model which should not (or can't) be exposed to the view. Especially when using short cuts like XML serialization to implement a web service.
Mark Renouf
+1  A: 

"Replace conditional with Polymorphism" is the name of the refactoring you are thinking of. Can't think of the name of the smell.

It is discussed in Martin Fowlers book Refactoring: Improving the Design of Existing Code.

Matthew Vines
+1  A: 

I don't know that it has a name (Type Switch CS?), but there's an interesting discussion of it (in Java) here.

LBushkin
+1  A: 

Faked polymorphism?

Tim Sylvester
or perhaps "monomorphism"?? ;-)
Austin Salonen
+3  A: 

It is called downcasting. link1 link2

Joel B Fant
No, that's what happens inside each if statement, not the name of the whole pattern.
Daniel Earwicker
But in order to downcast to a specific type, you have to test to make sure it will succeed. I see the above example as multiple instances of the same bad practice, rather than one.
Joel B Fant
Then you're missing the point - the solution is that the whole pattern gets replaced with a single call to a single virtual method, which is then implemented differently for each derived type.
Daniel Earwicker
+1  A: 

Its called, "Time to go buy a book and learn some design patterns". The Head First Design Patterns first chapter covers how to avoid this. Fruits extend Fruit, Prepare behaviors implementing PrepareBehavior. Inside your Fruit base class you could have (compose) whatever PrepareBehavior matches the fruit. (Strategy Pattern)

SwDevMan81
+1  A: 

I know what you're trying to ask, but the real problem is the design of your eat method and the architecture of your object hierarchy. There is no way to "eat a Fruit" in your architecture because each derived type of fruit has a different way of preparing itself to be eaten (one needs to be sliced, one needs to be pealed). The notion of eat (myFruit) itself is flawed.

You should change Fruit to have a PrepareToEat method. Each derived type of Fruit will implement PrepareToEat differently (bananas will peel, pineapples will slice). Then, eat can become an operation that can be done on a Fruit ...

public void Eat( Fruit f )
{ 
  f.PrepareToEat(); // implements the proper preparation peel, slice, etc.
  f.Eat();
}

Per comment: I see. It's an "incorrectly designed object hierarchy that violates the the Liskov Substitution Principle." Meyers writes extensively on this in Effective C++ and More Effective C++.

JP Alioto
Yes. I know the code is stinky. I was just asking what the smell is commonly called so I can go find more backing examples when advocating to have this cleaned up.
Mark Renouf
A: 

It's just method overload replacement for dynamic OOP languages.

Nothing special. Quite common and fairly legal.

Look at any serious JavaScript code to see it in action (f.e. ExtJS).

Thevs
A: 

It's an ad-hoc implementation of the Lisp TYPECASE macro.

Of course, in Lisp it's much less verbose, and there's no casting (everything is basically generic by default), so while it's relatively uncommon, it doesn't have quite the stigma it does in statically-typed languages:

(typecase fruit
  (banana (peel fruit))
  (pineapple (slice fruit))
  (strawberry ()))
(eat fruit)
Ken