Subtyping (design methodology)
- stepwise refinement by specialization
Specialization -- conceptual hierarchies
Implementation -- to realize a supertype
Combination -- multiple inheritance
Non-standard subtyping:
Generalization -- Win -> ColorWin
Variance -- Mouse -> Tablet [Pointing]
Limitation -- Deque -> Stack
Creativity and clear headed thinking are the most important ingredients of design!
slide: Design methodology
As we have seen previously, subtyping may be
used to realize a partially defined (abstract) type,
or as a means with which to add a specialization to a conceptual
hierarchy.
On a number of occasions, however, the need may arise
to employ what [HOB87]
called non-standard subtyping,
and which is also known as non-strict inheritance.
An example of non-strict inheritance is a derivation
that results in a generalization,
as occurs, for instance, when deriving a colored window
class from a base class window (supporting only
black and white).
We speak of a generalization since the type associated
with colored windows properly encompasses the collection
of black and white windows.
Yet from an implementation perspective, it may be convenient
to simply extend the code written for black and
white windows, instead of redesigning the class hierarchy.
Another example of non-strict inheritance is
when the derivation results in what may be called
a variant type, which occurs, for instance,
when deriving a pen or tablet class
from a class defining the behavior of a mouse.
The proper course of action in such cases is to
introduce an abstract class pointing device
from which both the mouse and tablet
classes may be derived (as proper subtypes).
Another, equally evident, violation of the subtyping
regime occurs when restricting a given class
to achieve the functionality belonging to the type of
the derived class.
A violation, since the requirement of behavioral
conformance demands that either new behavior is added
or already existing behavior is refined.
Actual limitation of behavior results in a behaviorally
incompatible class.
The standard example of limitation, originally given in [Snyder86],
is the restriction of a double ended queue into
a stack.
A better, and from a type perspective, perfectly legal
solution is to derive a stack from a double ended queue
by using private inheritance.
However, from a design perspective, the use of
private inheritance must be considered ill-practice,
since dependencies may be introduced in the object model
that remain invisible in a more abstract (type oriented)
description of the system.
Delegation, as in the handler/body idiom described
in section [2-handler], is to be preferred
in such cases since it makes the uses
relation explicit and does not compromise the type
system.
Example
When developing a class structure,
often the choice will arise as to whether to employ inheritance
or explicit delegation as a means to
utilize the functionality of a particular class.
Resource sharing -- delegation or inheritance
- class line : public list { ... }
- class line : private list { ... }
- class line { list words; ... }
slide: Sharing resources
The choices a designer may be confronted with when
developing a system or a class library may be illustrated
by a simple example.
Suppose you must develop a system that produces
a kwicindex.
A kwicindex contains for each line of text
all possible rotations, so that each word can be
rapidly searched for, together with the context in which
it occurs.
The example is taken from [VanVliet], who discusses
a number of alternative solutions from the
perspective of information hiding.
An object-oriented solution appears to be surprisingly
simple, in particular when a suitable library is available.
The two classes that are of interest are, respectively,
a class line, to represent each line of text,
and a class index to represent the lines
generated for the kwicindex.
slide: Representing lines
The class line is simply a list of words (strings)
that can be rotated.
The actual implementation of a line is
provided by an instantiation of
a generic (template) class list,
that is contained as a data member,
to which the operations insert and rotate
are delegated.
Instead of incorporating a member of type ,
the line class could have been made a descendant
of the class , as indicated below
class line : public list {
...
};
However, this would make the type line a subtype
of , which does not seem to be a wise decision.
Alternatively, private inheritance could have been employed
as in
class line : private list {
...
};
But, as we have noted before, private inheritance
may also introduce unwanted dependencies.
Explicit delegation seems to be the best solution,
because the relation between a line object
and a list of strings is best characterized
as a uses relation.
}
slide: Generating the index
For the index class, a similar argument
as for the line class can be given as to
why delegation is used instead of public or private
inheritance.
Actually, the most difficult part of the code
is the function defining how to compare two lines
of text that is needed for sorting the index.
Comparison is based on the lexicographical order
as defined below.
The ease of implementing both the line and index
classes depends largely upon the availability of a
powerful list template class.
In the example, we employed
the functions to rotate
the elements in the list, and
to sort the elements in the list.
The function was used
to determine the relation between elements
of the list.
The actual library employed for this example
is the splash library, which is discussed in
section [libraries].