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)]