Instructor's Guide
intro,
paradigms,
comparison,
design,
prototypes,
architectures,
summary,
Q/A,
literature
According to [Wegner87], much of the confusion
around the various features of object-oriented
programming languages arises from the fact that
these features are largely interdependent,
as for instance the notion of object and class on
the one hand,
and the notion of class and inheritance on the other.
To resolve this confusion, [Wegner87] proposes
a more orthogonal approach to characterize the
various features of object-oriented languages,
according to dimensions that are to a large extent independent.
See slide [5-orthogonal].
Orthogonal approach
- objects -- modular computing agents
- types -- expression classification
- delegation -- resource sharing
- abstraction -- interface specification
slide: Orthogonal dimensions
The features that constitute an object-oriented
programming language in an orthogonal way are,
according to [Wegner87]: objects, types,
delegation and abstraction.
Objects
are in essence modular computing agents.
They correspond to the need for encapsulation in design,
that is the construction of modular units to
which a principle of locality applies (due to
combining data and operations).
Object-oriented languages may, however, differ in
the degree to which they support encapsulation.
For example, in a distributed environment a
high degree of encapsulation must be offered,
prohibiting attempts to alter global variables
(from within an object) or local instance
variables (from without).
Moreover, the runtime object support system must
allow for what may best be called remote method invocation.
As far as parallel activity is concerned, only a few languages provide
constructs to define concurrently active objects.
See section [Active] for a more detailed discussion.
Whether objects support reactiveness,
that is sufficient flexibility to respond safely
to a message, depends largely upon (program) design.
[Meyer88], for instance, advocates a {\em shopping list
approach} to designing the interface of an object,
to allow for a high degree of (temporal) independence
between method calls.
Types
may be understood as a mechanism for expression classification.
From this perspective, Smalltalk may be regarded
as having a dynamic typing system:
dynamic, in the sense that the inability to evaluate an expression
will lead to a runtime error.
The existence of types obviates the need to have classes,
since a type may be considered as a more abstract description of the
behavior of an object.
Furthermore, subclasses (as may be derived through inheritance)
are more safely defined as subtypes in a polymorphic
type system. See section [flavors].
At the opposite side of the type dimension we find the statically typed languages,
which allow us to determine the type of
the designation of a variable at compile-time.
In that case, the runtime support system need not carry any type information,
except a dispatch table to locate virtual functions.
Delegation
(in its most generic sense)
is a mechanism for resource sharing.
As has been shown in [Lieber], delegation subsumes inheritance,
since the resource sharing effected by inheritance may easily be mimicked
by delegating messages to the object's ancestors
by means of an appropriate dispatching mechanism.
In a narrower sense, delegation is usually understood as a more
dynamic mechanism that allows the redirection of control dynamically.
In addition, languages supporting dynamic delegation
(such as Self) do
not sacrifice dynamic self-reference.
This means that when the object executing
a method refers to itself, the actual context
will be the delegating object.
See section [prototypes] for a more detailed discussion.
In contrast, inheritance (as usually understood in the context of classes)
is a far more static mechanism.
Inheritance may be understood as (statically) copying the code
from an ancestor class to the inheriting class
(with perhaps some modifications),
whereas delegation is truly dynamic in that messages are dispatched
to objects that have a life-span independent of the dispatching object.
Abstraction
(although to some extent related to types)
is a mechanism that may be independently applied to provide
an interface specification for an object.
For example, in the presence of active objects
(that may execute in parallel)
we may need to be able to restrict dynamically the interface of an object as specified by its type
in order to maintain the object in a consistent state.
Also for purely sequential objects we may impose a particular protocol of interaction
(as may, for example, be expressed by a contract) to be able to
guarantee correct behavior.
Another important aspect of abstraction is protection.
Object-oriented languages may provide (at least) two kinds
of protection.
First, a language may have facilities to
protect the object from illegal access by a client (from without).
This is effected by annotations such as private and protected.
And secondly, a language may have facilities to protect the object
(as it were from within) from illegal access through delegation
(that is by instances of derived object classes).
Most languages support the first kind of protection.
Only few languages, among which are C++ and Java, support the second kind too.
The independence of abstraction and typing may further
be argued by pointing out that languages
supporting strong typing need not
enforce the use of abstract data types having a well-defined
behavior.