Home
Up
Intro
Contents
Chapter
1
2
3
4
5
6
7
8
9
10
Design
Assert
Timing
EBNF
Report
Pas
Last Changed: July 12th, 1997
This is a conversion from Oberon text to HTML, and from German to English. The converter software is still under development,
and some features or information may be missing in this converted version.
HTML hypertext facilities are not yet active in this document.
To exploit the interactive facilities, use Oberon System 3 and the source of this text,
available for download using binary ftp as Oberon System 3 archive.
The converter from German to English is still under development as well.
A previous version is also available for Oberon V4.
To access this and other additional material use
ftp.
For the convenience of our students, most of this information and the related material is available
in German as well.
Introduction to Oberon
The Oberon Programming Language
9 Objekts and Messages
Oberon modules can be extended
- even a long time after the development of the original module is finished,
and possibly also during run time. The most impressive example is Oberon
itself. For example Oberon knows the abstract type Display.Frame. A specific
implementation is TextFrames.Frame which is the basis of all text frames.
This implemented in Oberon as a standard model. In the course of the time
variants have been added, which offer further possibilities, without modifying
the original module Display. A long time after development of the standard
Oberon Frames it is still possible to redefine Frames to add possibilities
which had not been thought of at first - for instance Frames, which use active
Internet connections or audiovisual possibilities. Try
Desktops.OpenDoc
"htt://xxxx"
where xxxx is your favorite web site to get an idea of the possibilities.
This extensibility does not need any new language constructs, but uses only
by now known language characteristics - essentially the extensibility
of abstract data types, which we have seen already. This is used for a particular
programming style, called object-oriented programming.
Object-oriented programming can be contrasted with procedural programming.
Procedural programming in the simplest case defines a program step by step.
With object-oriented programming however we assume that each object has methods
specifying how to react to a request. Orientation is not focussed on individual
program steps, but on requests to be addressed to specific objects. A Display.Frame
for instance can get the request to show itself, or to change its size or...
The necessary reaction can be different depending upon the type of the display
frame, and display frames of various types may be used as the application
might require. We speek of polymorphism here.
The information, which is necessary to respond to a request, may vary. A
text frame needs information about the text, which is to be displayed, while
a network frame needs only an appropriate network reference, and an audio
controller again may need quite different information. With object-oriented
programming status information and references to methods how to respond to
a request are encapsulated in the definition of the object.
For example the declaration of Display.Frame in the module Display is essentially
based upon
Frame*=POINTER
TO FrameDesc;
FrameDesc*=RECORD
....
X*, Y*, W*, H*: INTEGER;
handler*: PROCEDURE;
...
END;
A TextFrames.Frame however has these entries, and additionally references
to the text. With the possibility of extending data types, given by the Oberon
language, we need not repeat the information already used with Display.Frame,
but can extend these in a module TextFrames. The data types defined as extension
inherit all entries of their predecessors, and can extend these by
new specific fields.
FrameDesc*=RECORD(Display.FrameDesc)
text*: Texts.Text;
...
selbeg*, selend*:
Location (* current selection *)
END;
Display.Frame has a procedure called "handler".
This procedure is used to determine the reaction of variables of type Display.Frame
to various requests: Various kinds of requests can be made at variables of
type Display.Frame. Therefore we must parametrize the handler procedure,
in order to have a possibility of indicating the type of request. An alternative
would be to introduce a separate procedure for each type of request - not
a very practicable solution and not extensible. The "Slot" for the procedure
"handler" is inherited form Display.Frame to the type TextFrames.Frame, and
TextFrames.Frame to provided ways to respond to additional types of requests.
The idea to have a separate procedure for each request type is ruled out
if we want extensibility. In the same way, an enumration of all request types
using one of the basic types is ruled out, since it would permit no extensibility
later. Finally it is to be kept in mind that requests themselves can need
parameters - for example information about start and final position for a
selection to be processed. Again the way out are extensible data types. The
convention in Oberon is to define a type of procedure based on the following
declaration:
Handler*=PROCEDURE(frame:
Frame; VAR M: Msg);
with
Msg*=RECORD
END;
and using this declaration in module Display
Frame*=POINTER
TO FrameDesc;
FrameDesc*=RECORD
....
X*, Y*, W*, H*: INTEGER;
handler*: Handler;
...
END;
The abstract type Msg does not contain any information - except the information
which is implicitly contained in the type declaration. As RECORD declaration
it is extensible. We can define derived types. Like the basic declaration
these do not need to carry information data, as long as it is not required
by our application. For example we can define:
PrintMsg*=RECORD
(Msg) END;
The only information which we need are that a PrintMsg is a well-defined
request, distinct from a general abstract request of the type Msg. At execution
time, when the implementation of the handler procedure is executed, it is
identified using a query of the type
IF M IS PrintMsg
THEN
(* do everything
for printing*)
ELSIF M IS ... (*andere Msg-Typen
*)
END;
If we prefer to have additional information about the number of copies
to prepare, we would have used for example
PrintMsg*=RECORD
(Msg)
copies: INTEGER
END;
To make use of this abstract construction, we need a certain protocol. With
adaptations depending on the specific type and application, we define a procedure
MyHandler, say,
PROCEDURE MyHandler*(frame:
Frame; VAR M: Msg);
BEGIN
IF frame IS MyFrame
THEN
IF M
IS ...
ELSIF...
ELSE...
END;
ELSIF ... ELSE...
END;
END MyHandler;
For example for a frame of type MyFrame, we generate a new instance
using NEW
VAR thisFrame:MyFrame;
...
NEW(thisFrame)
...
and initialize it as
thisFrame.handler:=MyHandler
... (* initialize other fields
for MyFrame *)
If we identified an object obj as target of a request, specified by a message
M sent to obj, we know that
obj.handler
contains the methods necessary to hande the request. We need not inspect
the object first. A call as
obj.handler(obj,
M);
leads to an execution of the specific instructions as requested.
This protocol is flexible and has all possibilities for extensions. Usually
we will have one handler procedure for each object type. The protocol however
is so flexible that it permits different handlers for objects of a type.
We can modify the run time behaviour of a system in a flexible way.
Oberon-2 permits another simplified implementation model, however at the
loss of flexibility. In place of binding the methods to individual objects,
a method can be bound to object types bound in Oberon-2. Then the compiler
takes over the function to initialize the method slot. To allow this, an
addition target parameter is used. We have already seen this target parameter
in the syntax of a procedure declaration.
Procedure Syntax:
PROCEDURE
[*] [target] procedure name [formal parameter
list];
declaration
sequence
[BEGIN
statement
sequence ]
END procedure name;
The syntax for the target parameter is
(
[VAR] name: type )
If a target is indicated, this signals that the target type needs
a method. The method field is not listed in the type declaration, but added
automatically by the compiler. Each variable of the target type is supplied
automatically with the appropriate method, as implemented by the procedure.
The procedure is now bound to the type, not to individual variables of this
type. Within the procedure body the target parameter can be used like every
other parameter.
With type bound procedures it is possible to use information about the type
hierarchy. This can be used through "up calls": if x is a variable of the
type T2 with a method handler, and T2 is an extension of type T1, the x.handler
calls the appropriate method of T2, and x.handler^ calls the corresponding
method of its predecessor, i.e. T1. The compiler keeps track of the type
hierarchy and can find this method. A usual construction is
PROCEDURE (obj:
T2) handler(obj:T1;VAR M:Msg);
BEGIN
IF M IS ..(* new
message types of the extension T2 *)
THEN
ELSE obj.handler^(obj,M)(*
up call to handle messages
which
are already known to T1 *)
END
END handler;
In contrast to the first method, with type bound procedures all object of
one type share the same procedures. It is not possible to adjust them for
specific objects.
Literature: J.L. Marais: Extensible Software Systems in Oberon. Journal of
Computational and Graphical Statistics, 5.3 (1996) 284-298
Introduction to the Oberon programming language. ItO/Ch09.Text
gs (c) G. Sawitzki, StatLab Heidelberg
<http://statlab.uni-heidelberg.de/projects/oberon/intro/>
Home
Up
Intro
Contents
Chapter
1
2
3
4
5
6
7
8
9
10
Design
Assert
Timing
EBNF
Report
Pas