Let us now illustrate how to construct the most general classes and metaclasses of the class hierarchy. The purpose of this exercise is twofold. First, it allows us to experiment with different variations of the hierarchy. Second, it gives us first hand experience with the problems of dealing with the cyclic dependencies among the fundamental classes in an object-oriented programming environment.
The class hierarchies to be simulated are shown in figure .
Figure: A sample class and metaclass hierarchy.
A and B
are two sample classes defined by the user. Object is the root of both the class hierarchy and the metaclass hierarchy. As in Smalltalk, I will assume that the metaclass hierarchy is parallel with the class hierarchy. As a matter of naming, a metaclass of a class X is called X-class . Furthermore the class class-class
is an abstract class, which is a superclass of all metaclasses. Metaclass is the class of the metaclasses (i.e., the metaclasses are considered as instances of metaclass .) In turn, metaclass
is considered to be an instance of itself.
In appendix the full details of the classes and the metaclasses
are listed. Here I will only concentrate on the following aspects:
As can be seen from figure ,
object
is an indirect superclass of object-class
.
Thus, object
must exist at the time object-class
is instantiated.
On the other hand, object
is an instance of the
metaclass object-class
.
Consequently,
object-class
must exist at the time object
is instantiated.
This cyclic dependency means
that it isn't possible to create object
and object-class in the same way that we later will create, say, A and A-class .
The overall strategy for the construction of the class hierarchy
in figure can be described in the following way:
refers to the instance of class-class created in step 3.
In the rest of this section I will show some more details
of the construction of the hierarchy from figure .
Readers who are not interested in these details can skip
the rest of this section.
First, the temporary version of the metaclass object-class is defined:
(define (object-class-temporary) (let ((self nil) (super ())) ; assigned in the method fix-super (define (fix-super super-part) (set! super super-part) 'done) (define (instance-description) (let ((self nil) (super ()) ; empty list because root of hierarchy ...))) ... (set! self outer-dispatch) self))
The method fix-super is supposed to be activated on an instance of object-class-temporary , in order to make the connection to the more general parts of an object-class instance. Class-class is defined in the following way:
(define (class-class) (let ((self nil) (super (new-instance-part object))) (let ((instances () )) ;; a list of instances made by new (define (new) ) ; described in section 5.3 ... (set! self outer-dispatch)) self))
We are now in a position where we can create the class object by instantiating the temporary metaclass object-class-temporary
(define object (new-part object-class-temporary)) (send object 'fix-super (new-part class-class)) (virtual-operations object)
The first line sets up an object without a link to a super class. This creates an object which among other messages responds to fix-super . Next we send the message fix-super to object with an instance of class-class as a parameter. Hereby the super part of object becomes a class-class part.
When we in the following instantiate object-class
(in the process of instantiating a ``user defined'' metaclass) we certainly expect the instantiation to have an object -part, a class-class -part, and an object-class part. Therefore we substitute the definition of object-class-temporary
with
(define (object-class) (let ((oc (new-part object-class-temporary))) (send oc 'fix-super (new-part class-class)) oc))