Towards an orthogonal approach -- type extensions

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


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.