In fact, you said it was good enough.
The truth is that the “instance” of the comb almost always represents a bad idea (an exception occurs, for example, when marshaling or serializing, when for a short period of time you may not have all the type information at hand.) As Josh says, this sign of a bad class hierarchy otherwise.
What you know is a bad idea, that it makes the code fragile: if you use it and the type hierarchy changes, then it probably breaks this crest instance in all its place. What more, you lose the benefits of strong typing; the compiler cannot help you by catching errors ahead of time. (This is somewhat similar to the problems caused by types in C.)
Update
Let me expand this a bit, as from the comment it seems like I'm not entirely clear. The reason you use a type in C, or instanceof , is because you want to say "as if": use this foo , as if it were a bar . Now in C there is no information about the type of runtime, so you just work without a network: if you write something, the generated code will treat this address as if it contained a certain type, no matter what, and you should just hoping this will lead to a runtime error, rather than quietly corrupting something.
Duck seal only raises this rate; in a dynamic, weakly typed language like Ruby or Python or Smalltalk, this is all an untyped link; you capture messages at runtime and see what happens. If he understands a specific message, he "walks like a duck" - he processes it.
This can be very convenient and useful because it allows amazing hacks to assign a generator expression to a variable in Python or a variable block in Smalltalk. But this means that you are vulnerable to run-time errors that can be fixed at compile time by a strongly typed language.
In a strongly typed language such as Java, you cannot, strictly speaking, have duck print: you must tell the compiler which type you are going to process something. You can get something like duck print using type casting so you can do something like
Object x;
Now at runtime, you are fine as long as x is a kind of FoodDispenser in [1] or MissleController in [2]; otherwise an arrow. Or unexpectedly, without a boom.
In your description, you protect yourself by combing else if and instanceof
Object x ; // code code code if(x instanceof FoodDispenser) ((FoodDispenser)x).dropPellet(); else if (x instanceof MissleController ) ((MissleController)x).launchAt("Moon"); else if ( /* something else...*/ ) // ... else // error
You are now protected from runtime errors, but you are responsible for doing something reasonable later on else .
But now imagine that you are making changes to the code so that "x" can accept the types "FloorWax" and "DessertTopping". Now you have to go through all the code and find all instances of this comb and change them. Now the code is fragile - changes in requirements mean a lot of code changes. In OO, you are trying to make the code less fragile.
OO's solution is to use polymorphism instead, which you can think of as a type of limited set of ducks: you define all the operations you can trust. You do this by defining an excellent class, possibly abstract, that has all the methods of the lower classes. In Java, such a class is best expressed by an "interface", but it has all the properties of a class type. In fact, you can see the interface as a promise that you can trust a particular class to act as if it were another class.
public interface VeebleFeetzer { }; public class FoodDispenser implements VeebleFeetzer { } public class MissleController implements VeebleFeetzer { } public class FloorWax implements VeebleFeetzer { } public class DessertTopping implements VeebleFeetzer { }
All you have to do is use the link to VeebleFeetzer, and the compiler will explain it to you. If you add another class that is a subtype of VeebleFeetzer, the compiler will select this method and check the arguments in the deal
VeebleFeetzer x;