Is-a vs. is-like-a relationships
There’s a certain debate that can
occur about inheritance: Should inheritance override only base-class
functions (and not add new member functions that aren’t in the base
class)? This would mean that the derived type is exactly the same type as
the base class since it has exactly the same interface. As a result, you can
exactly substitute an object of the derived class for an object of the base
class. This can be thought of as pure
substitution, and it’s often referred to as the
substitution principle. In a sense, this is the
ideal way to treat inheritance. We often refer to the relationship between the
base class and derived classes in this case as an is-a relationship,
because you can say “a circle is a shape.” A test for
inheritance is to determine whether you can state the is-a relationship about
the classes and have it make sense.
There are times when you must add new
interface elements to a derived type, thus extending the interface and creating
a new type. The new type can still be substituted for the base type, but the
substitution isn’t perfect because your new functions are not accessible
from the base type. This can be described as an is-like-a relationship;
the new type has the interface of the old type but it also contains other
functions, so you can’t really say it’s exactly the same. For
example, consider an air conditioner. Suppose your house is wired with all the
controls for cooling; that is, it has an interface that allows you to control
cooling. Imagine that the air conditioner breaks down and you replace it with a
heat pump, which can both heat and cool. The heat pump is-like-an air
conditioner, but it can do more. Because the control system of your house is
designed only to control cooling, it is restricted to communication with the
cooling part of the new object. The interface of the new object has been
extended, and the existing system doesn’t know about anything except the
original interface.
Of course, once you see this design it
becomes clear that the base class “cooling system” is not general
enough, and should be renamed to “temperature control system” so
that it can also include heating – at which point the substitution
principle will work. However, the diagram above is an example of what can happen
in design and in the real world.
When you see the substitution principle
it’s easy to feel like this approach (pure substitution) is the only way
to do things, and in fact it is nice if your design works out that way.
But you’ll find that there are times when it’s equally clear that
you must add new functions to the interface of a derived class. With inspection
both cases should be reasonably
obvious.