Objects and Their Messages
Now we come to creating an object of class Rect
and
sending messages to that object so it can select the methods to execute.
To create an object of class Rect
, the syntax is simply
the name of the class followed by the name you want to assign to the
object. For an object named "Box1
" of class
Rect
, the statement would be:
Rect Box1
That's all there is to it! By creating this object, you have added a
new Mops word, "Box1
" to the dictionary in memory.
Recalling our definition of Rect
from the previous
lesson, you can visualize the object in memory to look like:
Zeros are placed in the instance variable cells when the object is created, and they are holding space for numbers whenever the object receives a message to put data there.
When you type a Mops message in a program, it has three parts to it: the parameters, selector, and receiver.
Parameters are the numbers to be passed to the operation. They are placed on the parameter stack just like parameters in [Chapter1.html#Enter Lesson 1]. Not all messages have parameters, of course. Some operations don't require any numbers be passed to them.
The second part, the selector, is actually the name of the method containing the operation you want the object to perform. In other words, the object "selects" which method of its class is to be put to work; the object matches the message's selector with the method in the object's class (or up the superclass hierarchy if there is no match in the immediate class).
The last part of a message, the receiver, must be the name of an object. It is the "thing" on which you want to perform the operation specified by the selector. In the accountant metaphor used in the preceding lessons, the receiver is the name of the accountant who is to prepare the returns.
Since Box1
is an object of our rectangle class, you can
send a message to it that selects one of the methods defined in class
Rect
. If you send the message
300 20 400 100 put: Box1
you put the coordinates 300,20 and 400,100 into the data cells reserved
for TopLeft
and BotRight
in the
Box1
object. After all, that's what the
PUT:
method in Box1
's class does: it
places two sets of two parameters into an object's data cells.
If, at some future time, you create a new object of class
Rect
, called "Box5
",
Box5
's data cells would be empty at first. A separate
PUT:
message would have to be sent to
Box5
to place Box5
's coordinates in
that object's data cells. This is how objects maintain private data.
Now, to draw the objects on the screen with Box1
, you
need to send another message, one that calls upon the
DRAW:
method of class Rect
. The
message would be:
draw: Box1
If you were defining class Rect
from scratch, you could
also define a new method that combines the functions of two methods into
one. Then, a single message would take care of both the
PUT:
and DRAW:
methods. For this to
happen, you need a way for the new method to look up the methods in the
same class. That's where a message receiver called
SELF
comes in handy. With the new method
(DISP:
) the class looks like this:
:class RECT super{ object }
record
{ point TopLeft
point BotRight
}`
:m PUT: put: BotRight put: TopLeft ;m ( l t r b -- )
:m DRAW: ^base FrameRect ;m
:m DISP: put: self draw: self ;m
;class
The DISP:
method contains the messages, 'put: self
' and 'draw: self
'. The 'put: self
' message is saying, Do to the current object everything
that the PUT:
method in this class does. The same goes
for 'draw: self
'. If you had intended one of these
messages to look up a method in Rect
's superclass, the
receiver would have been SUPER
(as in 'put: super
').
Something important happens when you have the 'put: self
message inside the DISP:
method. The
DISP:
method now expects to find four integers passed
along with any message bearing its selector, just like the actual
PUT:
method that executes the storage command requires
four integers. Therefore, to both locate and draw Box1
on the screen, you would send the message:
12 10 100 50 disp: Box1
If you want to try this, you'll have to have a window to display
Box1
in, as you did in the [../Overview/Chapter1.html
Introduction]. So first copy the Rect
definition
(above) to the Mops window (either by typing it in or by copying and
pasting it). Then select the whole of the definition (by dragging with
the mouse). Then hit the ENTER key. This will cause all the selected
text to be executed. In this case, since the code is a class definition,
the result of executing the code will simply be to define the class
Rect
. Nothing will seem to happen, but the definition
for Rect
will have been entered into Mops' dictionary
and will be ready to do your bidding.
Now type and execute this:
Window ww
test: ww
Click back on the Mops window and move things around so you can see both
the Mops window and ww
, then type and execute
Rect Box1
set: ww
12 10 100 50 disp: Box1
and your Rect
instance (Box1
) should
appear in the window ww
.
[#Summary Summary]
Before taking one more step, let's summarize. Creating a Mops program entails the following steps: defining classes; creating objects that are instances of those classes; and then sending messages to those objects. Building a hierarchy of classes starts with the broadest class and works toward the more specific classes, with subclasses inheriting the characteristics of their superclasses.
The following diagram will help you visualize the structure of the program example detailed in this chapter. It graphically portrays the relationships between the classes and objects discussed above.
Given this framework, when you issue the message '12 10 100 50 disp: Box1
', the parameters fill Box1
's data
cells held in reserve when Box1
was created.
The characteristics of the data had already been determined by the ivars
TopLeft
and BotRight
; the
characteristics of each those ivars had been likewise determined by the
ivars X
and Y
, which, in
turn, had been defined by the methods of their defining class, class
Int
.
Therefore, you probably recognize that the relationships in Mops classes and objects are on multiple levels. On the one hand, you have the relationships between superclasses and subclasses. On the other hand, you have the relationships between ivars and their defining classes. Both relationships cascade through the hierarchy of a Mops program independently of each other. That will become even clearer as we make one further extension to the example above.