I’m currently reading ‘Head first design patterns’ and I already have a few questions on the first chapter of the book.
This chapter introduces the ‘prefer composition over inheritance’ design principle, by explaining the ‘Strategy-Pattern’. My question isn’t about the pattern, but rather about a more basic design decision in one of the examples:
There is an abstract Duck
class, from which a hand full of other duck classes (like e.g. ‘Rubber-‘ or ‘Redhead-Duck’) inherit. All of these ducks have a display
and quack
method.
The display
method is what should be differernt in every special duck class (because every duck looks different, as the book says).
abstract class Duck
{
public void display();
public void quack()
{
//Quack here
}
}
class RubberDuck extends Duck
{
@Override
public void display()
{
//Display a 'Rubber-Duck'
}
}
class RedheadDuck extends Duck
{
@Override
public void display()
{
//Display a 'Redhead-Duck'
}
}
But my question is: Wouldn’t it make much more sense to have a field inside a (non-abstract) Duck class, which contains the displaying information (a picture or a 3D-model for example) rather than inheriting from an abstract duck class?
class Duck
{
private String duckName;
private Picture duckPicture;
public Duck(String name, Picture picture)
{
this.duckName = name;
this.duckPicture = picture;
}
public void quack()
{
//Quack here
}
public void display()
{
//Show duck here, according to the 'duckPicture' field
}
//More methods here, maybe a getter for the 'duckName' field
}
What’s your opinion on this? Does it make sense to have an abstract duck class and inherit from it or do you prefer the second solution?
2
You have to understand that examples are almost always oversimplified to such an extent that techniques like inheritance look like overkill.
If the real-world code were of a similar complexity as the Duck
hierarchy, with each class only differing in which picture/text they show, then you would have a case.
But in reality, the real-world code is much more complex, usually with at least a handful of methods that have radically different implementations across the classes. In that case, it doesn’t work anymore to catch the variation in a few fields.
5
If you assume that display operation will only send rasterized image to display device, then you are right – one implementation of display method would suffice.
But, if you have an abstract method for display operation, this method can also do some implementation-specific stuff like:
- create the image of a duck using openGL API,
- update other fields of a duck (e.g. isDisplayed),
- call Display on sub-objects (e.g. DuckWing),
- etc.