OO Concept: Interfaces & Abstract Classes

In the OO topic
Objects & Classes
the concepts of
objects
and
classes
were introduced. We saw an illustration of the basic ideas by starting to
develop a Track class. In
Inheritance
it was shown how classes could evolve by building more specific ones out of
more general ones with the
subclass
designer choosing what features to retain
from the
base class,
what features to modify and what new features to add. We took our Track class
and evolved ChargedTrack and NeutralTrack from it. We decided to let
NeutralTrack inherit the Propagate
member function
from Track but decided to replace
(or overload)
in ChargedTrack. Finally, in
Virtual Functions &
Polymorphism
it was explained how to make embedded base class objects delegate
responsibility for certain member functions to the subclass object in which
they are embedded. In this way functionality between cooperating classes can move
up to subclasses
(polymorphism)
or down to base classes
(inheritance).
We saw this illustrated with Track and ChargedTrack. The Track class
cooperated by designating Propagate as virtual which permits ChargedTrack to
take control when the Propagate
message
was sent to a Track object embedded in it. We further saw the power of this
concept, allowing us to write driver code to propagate tracks independently
of the propagation code.

It is this separation of the driver from the object, or in more general terms
the client from the server that will examined further in this
topic. When we decided to create ChargedTrack and NeutralTrack,
part of the reason was to allow two versions of the Propagate function. The
decision to have a new version for ChargedTrack but not for NeutralTrack was,
to be honest, a bit odd. In fact, to be very honest, it was contrived so we
could examine both cases!
It would have been far more natural to have provided a
separate version for NeutralTrack. So we end up with 3 Propagate functions:
Track::Propagate, ChargedTrack::Propagate and NeutralTrack::Propagate. Things
look bleak for Track:Propagate! When we come to create real tracks
they will either
be charged or neutral, so the Track object will only exist within ChargedTrack
or NeutralTrack objects, so Track::Propagate will always delegate its function
and the code will fall into disuse. Anyone
who maintains software will tell you that code that isn't used eventually goes
bad, even if the decay mechanism is not fully understood!
If its not used, it best to surgically remove it. So should Track be
stripped of Propagate? Well, no it shouldn't. Recall how useful it was when
writing driver code like this:-

It was the virtual function address table held inside the Track object that
kept the driver separate from the Propagate function and allowed the decisions
as to which function to call to be delayed until execution time. So we want
to keep the table but drop the code. This may sound odd but C++ allows us to
do just that, by declaring the Propagate function like this:-

virtual void Propagate() = 0;

This means that the function body, i.e. its implementation does not exist.
This is called a pure virtual function.
Once a class has such a function it is no longer possible to directly create
objects, as they are incomplete. Such classes are called
abstract base classes
and their objects only exist embedded within others.

When we have an abstract base class it means that we are dealing with concepts
that are so generic, or abstract, that no single implementation that can do
justice to the concept the classes embodies. The importance of such a class is
not so much in the services it directly provides but the
interface that decouples the
client from the server, allowing each to evolve separately whilst retaining
the relationship. This can be represented by this diagram:-

As has been said before, at its heart, OO is all about interfaces and here we
see the just how powerful it can be in decoupling client and server software
to maximise the potential for development and reuse.

Example of Abstract Classes and Interfaces

Containers are objects that can hold other objects, and is the subject of the OO
topic
Containers.
For now all that need be known is that there can be a number of different
containers such as array, lists and trees. They all evolve from the generic
concept of a collection, part of whose model is the iterator. An iterator is
an object that can sequentially scan a collection, returning each member in
turn. ROOT's container classes all evolve from
TCollection
and all its
iterators from
TIterator.
Both these classes are abstract, they embody the generic concepts of collection
and iterator and the intimate relationship between them.
Go Back to the
The C++ Crib Top Page

If you have any comments about this page please send them to
Nick West