In general, it's difficult to strictly maintain a contract between two distinct concrete objects.
Inheritance becomes really easier and more robust when we deal with generic behavior.
Imagine that we want to create a class named Ferrari
and a subclass named SuperFerrari
.
Contract is: turnTheKey()
, goFast()
, skid()
At first glance, sounds like very similar class, with no conflict. Let's go with inheritance of these both concrete classes.
However, now we want add a feature to SuperFerrari
: turnOnBluRayDisc()
.
Problem: Inheritance expects a IS-A relationship between components. With this new feature, SuperFerrari
is not a simple Ferrari
any more with its own behavior; it now ADDS behavior .
It would lead to some ugly code, with some cast needed, in order to choose the new feature while dealing with a Ferrari
reference initially (polymorphism).
With an abstract/interface class in common to both classes, this problem would disappear since Ferrari
and SuperFerrari
would be two leafs, and it's more logic since now Ferrari
and SuperFerrari
should not be consider as similar (not a IS-A relationship any more).
In short, derive from concrete class would lead in many cases to a poor/ugly code, less flexible, and hard to read and maintain.
Making a hierarchy driven by abstract class/interface allow concrete children classes to evolve separately without any issues, especially when you implement some subhierarchies dedicated to a special amount of concrete classes without boring others leafs, and still benefit from polymorphism.