Inheritance and Delegation

The following example illustrates a number of concepts. Firstly, how to use SICStus Objects for defining traditional classes a la Smalltalk, or other traditional object oriented languages. Secondly, how to create instances of these classes. Finally, how to access instance variables.

The concept of instance variables is readily available as the variables belonging to the instances created dynamically and not to the class of the instances. For example, each instance of the class point will have two instance variables, x and y, represented by the attributes x/1 and y/1. The traditional class variables are easily available by accessing the same attributes in the associated class.

Another issue is the pattern used to create new instances. For example, to create an instance of the class history_point, the following code is used:

             new(Instance, xy(IX,IY)) :-
                     super <: new(Instance, xy(IX,IY)),
                     Instance :: set(history([(IX,IY)])) &
     

Note that the delegation of new/2 to super is necessary in order to create an object whose super is history_point and not point.

The example shows how delegation can be effective as a tool for flexible sharing of concepts in multiple inheritance. Four prototypes are defined: point, history_point, bounded_point, and bh_point. The latter is a bounded history point.

An instance of the point class is a point that moves in 2-D space and that can be displayed. An instance of the history_point class is similar to an instance of the point class but also keeps a history of all the moves made so far. An instance of bounded_point is similar to an instance of point but moves only in a region of the 2-D space. Finally an instance of bh_point inherits most of the features of a bounded_point and a history_point.

The default inheritance does not work for the methods display/1 and move/2 in bh_point. Inheritance by delegating messages to both supers of bh_point results in redundant actions, (moving and displaying the point twice). Selective delegation solves the problem. Taken from [Elshiewy 90].

     point :: {
             super(object) &
     
             attributes([x(0),y(0)]) &
     
             xy(X, Y) :- get(x(X)), get(y(Y)) &
     
             new(Instance, xy(IX,IY)) :-
                     super <: instance(Instance),
                     Instance :: set(x(IX)),
                     Instance :: set(y(IY)) &
     
             location((X,Y)) :- <: xy(X,Y) &
     
             move_horizontal(X) :-
                     set(x(X)) &
     
             move_vertical(Y) :-
                     set(y(Y)) &
     
             move(X, Y) :-
                     <: move_horizontal(X),
                     <: move_vertical(Y) &
     
             display(Terminal) :-
                     <: xy(X, Y),
                     Terminal :: format('point at (~d,~d)~n',[X,Y])
             }.
     history_point :: {
             super(point) &
     
             attributes([history([])]) &
     
             new(Instance, xy(IX,IY)) :-
                     super <: new(Instance, xy(IX,IY)),
                     Instance :: set(history([(IX,IY)])) &
     
             move(X, Y) :-
                     super <: move(X, Y),
                     get(history(History)),
                     set(history([(X,Y)|History])) &
     
             display(Terminal) :-
                     super <: display(Terminal),
                     <: print_history(Terminal) &
     
             print_history(Terminal) :-
                     get(history(History)),
                     Terminal :: format('with location history ~w~n',
                                        [History])
             }.
     bounded_point :: {
             super(point) &
     
             attributes([bounds(0,0,0,0)]) &
     
             new(Instance, Coords, Bounds) :-
                     super <: new(Instance, Coords),
                     Instance :: set_bounds(Bounds) &
     
             set_bounds(Bounds) :-
                     set(Bounds) &
     
             move(X, Y) :-
                     <: bound_constraint(X, Y), !,
                     super <: move(X, Y) &
             move(_, _) &
     
             bound_constraint(X, Y) :-
                     get(bounds(X0, X1, Y0, Y1)),
                     :(X >= X0),
                     :(X =< X1),
                     :(Y >= Y0),
                     :(Y =< Y1) &
     
             display(Terminal) :-
                     super <: display(Terminal),
                     <: print_bounds(Terminal) &
     
             print_bounds(Terminal) :-
                     get(bounds(X0, X1, Y0, Y1)),
                     Terminal :: format('xbounds=(~d,~d), \c
                                         ybounds=(~d,~d)~n',
                                        [X0,X1,Y0,Y1])
             }.
     bh_point :: {
             super(history_point) &
             super(bounded_point) &
     
             new(Instance, Coords, Bounds) :-
                     history_point <: new(Instance, Coords),
                     Instance :: set_bounds(Bounds) &
     
             move(X, Y) :-
                     bounded_point <: bound_constraint(X, Y), !,
                     history_point <: move(X, Y) &
             move(_, _) &
     
             display(Terminal) :-
                     bounded_point <: display(Terminal),
                     history_point <: print_history(Terminal)
             }.
     tty :: {
             format(X, Y) :- :format(X, Y)
            }.
     
     point at (8,12)
     xbounds=(5,15), ybounds=(5,15)
     with location history [(8,12),(9,11)]