• Aucun résultat trouvé

An Example: Constrained Pens

Dans le document til Smalltalk (Page 69-83)

Sma/ltalk Fundamentals

2.4 CONTROL STRUCTURES WITH MESSAGE-PASSING

2.6.2 An Example: Constrained Pens

To illustrate subclassing and inheritance, we will implement a specialization of thc systcm class Pen called ConstrainedPen. A subset of the protocol for class Pen is shown in Fig. 2.10.

Class Pen class name superclass instance variables class variables comment

class methods instance creation

Pen BitBlt

frame location direction penDown SinArray

My instances can scribble on the screen, drawing and printing at any angle. Since I am a BitBlt, scribbling can be done with different source forms.

IlBW

Return an initialized instance of class Pen.

class initialization initialize

Initialize the class Pen.

instance methods initialization

defaultNib: widthlnteger

Nib is the tip of a pen. This is an easy way to set up a default pen where the source form is set to a black square whose sides are width Integer long.

Chapter 2 Smalltalk Fundamentals 53

accessing

direction

Answer the receiver's current direction; 0 is towards the top of the screen.

frame

Answer the rectangle in which the receiver can draw.

frame: eRectangle

Set the rectangle in which the receiver can draw.

Iocetion

Answer where the receiver is currently located.

moving cbwn

Set the state of the receiver's pen to down (drawing).

go: distance

Move the pen in its current direction a number of bits equal to the argument, distance. If the pen is down, a line will be drawn using the receiver's source form as the shape of the drawing brush. Otherwise, nothing is drawn.

goto:ePoint

Move the receiver to position aPoint. If the pen is down, a line will be drawn from the current position to the new one using the receiver's source form as the shape of the drawing brush. The receiver's set direction does not change.

honw

Place the receiver at the center of its frame.

north

Set the receiver's direction to facing the top of the display screen.

piece: ePoint

Set the receiver at position aPoint. No lines are drawn.

tum: degrees

Change the direction of the receiver by an amount equal to the argument, degrees.

Set the state of the receiver's pen to up as opposed to down (no drawing); i.e., off the drawing frame. This is different from north which causes the pen to point upward.

Figure 2.10 Class Pen.

Pens are the Smalltalk equivalent of turtles in Logo. A pen is an object that can draw within a specified rectangular frame (or window) on the Smalltalk display. Class Pen includes protocol to change the position of the pen on the screen, change the direction (in degrees) it is facing, and set the state of the pen.Ifthe pen is moved when the state of the pen is down (as opposed to up), the pen draws on the display. The default drawing nib of the pen is a single pixel. Nibs of other shapes, patterns, and sizes can be specified.

1:1111 !

To illustrate programming with pens, consider the following example method. Note that the Smalltalk screen coordinate system has its origin at the top left comer. The x-axis increasestothe right of the screen while the y-axis increases down the screen.

example

"Draw an equilateral triangle with sides of length 200."

I crayon I

"Creates a new instance of class Pen with a black nib that is 2 pixels wide and 2 pixels high. The initial direction of the pen is north, the drawing frame is the entire display screen, the initial location of the pen is at the center of the screen. and the state of the pen is down."

crayon~Pen new d.faultNib: 2. "get a pen with a medium size nib"

crayonup."stop drawing"

crayon goto: 3500250. "move to start point"

crayon down. "start drawing"

"Draw an equilateral triangle with sides 200 units long."

3 timeeRepeat: [crayon go: 200; turn: 1201

The specialized class ConstrainedPen is to be restricted so that instances can only move, and hence draw, in the horizontal and vertical directions. This new class of object might be useful if we were drawing flowcharts or constructing diagrams. To share code that already exists in class Pen, class ConstrainedPen should be a subclass of Pen.

ConstrainedPen automatically inherits the representation of all classes in the inheritance chain (see Fig. 2.11). The inheritance chain consists of classes Pen, BitBlt, and Object. The instance variables for Pen are frame, a rectangular area into which the pen is constrained to draw; location, a point representing the current position of the pen;

direction, a float representing the direction the pen is pointing; and penDown, a boolean describing the state of the pen. In addition, class variable SinArray, a table of sin values, is also inherited by ConstrainedPen. Pen is a subclass of class BitBlt - an extremely general class providing fundamental operations for displaying and modifying text and graphics. Pen is a subclass of BitBIt, so that it can inherit the operations for drawing lines on the display with different nib styles and also the operations to perform automatic clippingtothe frame of the pen. The instance variables for class BitBIt will be inherited by class ConstrainedPen, but we will not need to access them directly. We will inherit operations that manipulate them from class Pen. No additional instance variables are required for class ConstrainedPen.

It is instructive to consider which methods can be inherited without modification by ConstrainedPen. Since instances of class Pen have the same representation as instances of ConstrainedPen, we can inherit the class method new for creating instances. Similarly, the instance methods that access or modify the state of a pen; i.e., direction, frame, frame:

aRectangle, location, up, down, home, north, and defaultNib: widthInteger can also beinherited.

The methods involving movement of the pen need more careful consideration. There are three possible options for each method.

Inherit the method from a superclass.

Implement a modified form of the method.

The method is not appropriate for the class - make it an error to use it.

Chapter 2 Smalltalk Fundamentals 55

Object

)

I BitBlt instance variables

destForm sourceForm halIToneForm combinationRule destX

destY width height sourceX sourceY clipX clipY clipWidth clipHeight

.. ',.', .. ..."

,-I

Pen instance variables

frame location

direction ~

penDown ~B

class variables ~~

SinArray :~~

~~~~:«";'::" .. ...:.

I

ConstrainedPen

.'

Figure 2.11 Representation inheritance for class ConstrainedPen.

The instance methods goto: aPoint and place: aPoint in class Pen allow movement to random points. The first is clearly inappropriate for the class ConstrainedPen because it can result in something being drawn. The second, however, is legitimate because no drawing results. Therefore, we must ensure that mcthod goto: is not inherited. This can be achieved by taking advantage of the error handling protocol supported by class Object. We rc-implement the method in class ConstrainedPen (see Fig. 2.12) to generate an error mcssage; i.e., the body of the mcthod bccomes

self shouldNotlmplement

1'11I1 I

This message will eventually, through the inheritance chain, be found in class Object.

The result will be a standard error report that "although this message is appropriate for the superclass of the receiver, it is not appropriate for the class of the receiver."

Now, consider method turn: degrees for rotating the pen direction. It must be modified to constrain drawing in the vertical or horizontal directions. Since the pen can only move in these two directions, we could augment the instance protocol of ConstrainedPen with methods south, west, and east to allow the pen directiontobe changed. Method north can be inherited. We might also want to introduce special variations of turn: such as turnLeft and turn Right. Indeed, it might be more appropriateto add these to Pen rather than to ConstrainedPen since it is a useful generalization, but we won't pursue that here.

What to do with the method turn: is still unresolved. We cannot allow instances of ConstrainedPen to inherit the turn: method from class Pen. We could override the inheritance mechanism using self shouldNotlmplement, as described above. Alternatively, we could introduce a modified turn: message that constrains its argument to a multiple of 90 degrees. For the sake of illustration, we will choose the latter option. An obvious way of implementing the modified turn: method is to truncate the argument degrees to a multiple of 90 and invoke the turn: method in class Pen. However, we need some way of referring to the turn: method in the superclass. If we use the expression

self turn: degrees

the effect will be to invoke the turn: method in ConstrainedPen recursively. Smalltalk provides the pseudo-variable super to allow references to methods higher up in the inheritance chain.

2.6.3 The Pseudo-Variable super

Pseudo-variable super provides accesstomethods in the superclass chain even if the method has been redefined in the class. Like self, super refers to the receiver of the method.

However, when super is used, the search begins in the superclass of the class containing the method definition. Be careful - this is not always the same thing as starting the search in the superclass of the receiver.

The modified turn: message for ConstrainedPens making use of super is shown below.

turn: degrees

"The direction of the receiver is turned clockwise through an amount equal to the argument degrees. The argument is automatically truncated to a multiple of 90 degrees."

super turn: (degrees roundTo: 901

Now, consider method go: distance. At first sight, we might think that it can be inherited directly from class Pen because a pen's direction is constrained to horizontal or vertical movement. However, examination of the method reveals that it invokes the message goto:. The goto: message will be sent to the receiver of the go: message, a ConstrainedPen. But this message was previously overridden for constrained pens to make it an error. This example illustrates the fact that problems can arise with the inheritance of a method if the inherited method itself invokes methods that have been overridden in the

Chapter 2 Smalltalk Fundamentals

subclass. To achieve the desired effect, we must re-implement go: in ConstrainedPen as a clone of the go: in Pen, but with the self goto: reference replaced by super goto:.

go:distance

"Move the receiver in its current direction a number of bits equal to the argument, distance. If the pen is down, a line will be drawn using the receiver's source form as the shape of the drawing brush. Otherwise, nothing is drawn."

I angle newDirection I

angle+-directiondegreesToRadians.

newDirection+-anglecos@anglesin.

supergoto:newDirection *distance+ location Pen class methods

initialize

new :

instance methods

defaultNib: widthlnteger direction

frame: aRecumgle location

down go: distance goto: aPoint home north place: aPoint turn: degrees up

I

ConstrainedPen ;:

:::.

instance methods ::::

east (new protocol) :.~~

west (new protocol) :.~1

south (new protocol) ::

I

go: distance (modified) turn: degrees (modified) I:

j

goto: aPoint (overridden) place: aPoint (overridden)

Figure 2.12 Hierarchical inheritance of methods.

Fig. 2.12 summarizes the final method inheritance hierarchy for classes Pen and ConstrainedPen. Note that other changes are possible. For example, we might wish to introduce a direction: method to permit absolute settings of the direction. Methods east, west, and south could then be implemented using this new operation. We might also want to add additional turn operations like turnLeft, turnRight, and turnBack. Method turnLeft, for example, could simply consist of the code "self turn: -90". Using "self' instead of "super" would ensure that future changes (if any) to turn: in ConstrainedPen would be reflected in the new methods. Of course, many of these operations also make sense for standard pens. This suggests that some of the methods in ConstrainedPen ought to be migrated up into class Pen. When the classes affected are both user defined, this is a natural improvementtomake. When system classes are affected, more deliberation is needed. Unless the change is fundamental and important, it is generally safer to leave library classes alone since they might change from release to release. The full definition of class ConstrainedPen is shown in Fig. 2.13.

Class ConstrainedPen

class name superclass class methods examples

ConstrainedPen Pen

example

"Illustrates the use of constrained pens."

I quill I

quill~ConstrainedPennew.

quillhome; place: 300@300; down.

4 timesRepeat:[quillgo: 100; turnLeftJ

"ConstrainedPenexample"

instance methods moving

goto:aPoint

"This message is not appropriate for this object."

selfshouldNodmplernent go:distance

"Move the receiver in its current direction a number of bits equal to the argument, distance. If the pen is down, a line will be drawn using the receiver's source form as the shape of the drawing brush. Otherwise, nothing is drawn."

I angle newDirection I

angle~directiondegreesToRadians.newDirection~anglecos@anglesin.

supergoto:newDirection*distance+location south

"The direction of the receiver is set to face the bottom of the screen.·

direction~90

Chapter 2 Smalltalk Fundamentals 59

eest

"The direction of the receiver is set to face the right of the screen."

direction~0 west

"The direction of the receiver is set to face the left of the screen."

direction~ 180 tum: degrees

"The direction of the receiver is turned clockwise through an amount equal to the argument degrees. The argument is constrained to a multiple of 90 degrees by rounding."

superturn:(degreesroundTo:90) turnLeft

"The direction of the receiver is turned to the left90 degrees."

superturn:-90 tumRight

"The direction of the receiver is turned to the right90degrees."

superturn:90

Figure 2.13 Class ConstrainedPen.

2.6.4 Abstract Classes

The shaded classes Magnitude, Number, and Integer (see Fig. 2.9) are termed abstract classes.

Abstract class A class that specifies protocol but is unable to implement it fully because its subclasses may have different representations.

Because an abstract class does not fully implement its protocol, no instances of abstract classes may be created. The role of an abstract class is to specify the protocol common to all of its subclasses, with the subclass providing the implementation where no common implementation can be provided in the abstract class itself.

Class Magnitude is an abstract class used to describe objects that can be compared along a linear dimension. The subclasses of Magnitude are classes Character, Date, Number, and Time. The common protocol specified by the class Magnitude reflects the fact that all instances of each of the subclasses can be compared with one another using the relational operators. For example, we can ask a number if it is greater than another or we can ask a date if it is less than another, and so on. The instance protocol for magnitudes includes (among others) the operations

aMagnitude <

aMagnitude <=

aMagnitude >

aMagnitude >=

anotherMagnitude anotherMagnitude anotherMagnitude anotherMagnitude

Since the representations for instances of the subclasses Character, Date, Number and Time are clearly different, each subclass provides its own implementation for operations that are dependent on the representation. Operations that reference their representation directly

are pnmitIve operations - if the representation were changed. they would require modification. The implementation of primitive operations must be the responsibility of the subclasses.

For messages where it is the responsibility of a subclass to provide the implementation. an abstract class implements the method by generating an error message.

The subclassResponsibility protocol supported by class Object can be used to generate a message indicating that a subclass should have overridden the implementation of this method. This is useful when a new subclass is added and the programmer forgets to implement the entire protocol specified by the abstract superclass. Methods in the abstract class that must be re-implemented by subclasses should have the body

self subclassResponsibility

Non-primitive operations can be implemented in terms of other primitive and/or non-primitive operations and therefore can be implemented once in the abstract class. For exam-ple. in the case of magnitudes. only the primitive < operation is implemented by the sub-classes. Operations such as>and<=are non-primitive because they can be implemented in terms of<.They only need to be implemented once in the abstract class. as shown below.

comparing

<aMagnitude

"Answer whether the receiver is less than the argument."

i self subclassResponsibility

<=aMagnitude

"Answer whether the receiver is less than or equal to the argument."

ilself>aMagnitudel not

>aMagnitude

"Answer whether the receiver is greater than the argument."

i aMagnitude<self

>=aMagnitude

"Answer whether the receiver is greater than or equal to the argument."

ilself < aMagnitudel not

Abstract classes have an important role to play in Smalltalk and in object-oriented programming. As we have secn. they allow the protocol common to a collection of classes to be identified quickly. By browsing the abstract superclasses. for example. it is easy to determine what operations are common to all types of numbers. to all types of integers. and so on. Another benefit is that they can be used to maximize the sharing of code through inheritance. Consider the "Bricks" video game that was described at the beginning of Chapter1.An initial class hierarchy for the game is shown in Fig. 2.14.

Chapter 2 Smalltalk Fundamentals 61

Object

Video

Ball Brick Side Paddle

Game

Figure 2.14 Initial video game class hierarchy.

The problem with this class hierarchy is that it is impossible to specify a protocol commonto all game elements. A new class hierarchy incorporating two abstract classes is shown in Fig. 2.15. The VideoGameComponent abstract class ties together the game parts and allows subclasses to share a common representation and common operations. The MovingGameComponent abstract class allows a distinctiontobe made between dynamic and static game objects and allows the move operations to be shared by both the Ball and Paddle classes. Container classes for the bricks - Wall and for the left, top, and right sides - Sides, are also introduced.

Object

Video Game

Sides Side

Ball Paddle

Wall Brick

2.7 SUMMARY

Figure 2.15 Class hierarchy with abstract classes.

In this chapter, we have described how the fundamental concepts of object-oriented languages - objects, messages, classes, and inheritance, manifest themselves in Smalltalk. In particular, we have discussed

the use of literalstodescribe numbers, symbols, characters, strings, and arrays, the use of variables and the assignment operation in Smalltalk,

1'.1

• the construction of message expressions using unary, keyword, and binary messages,

• the concepts of dynamic binding and overloading,

• storage allocation and garbage collection,

• control structures via message-passing,

• class versus instance protocols,

• named versus indexed instance variables,

• private versus shared variables,

• the pseudo-variables self and super,

• subclassing,

• representation and method inheritance, and

• the use of abstract classes.

2.8 EXERCISES

Unless you are familiar with the Smalltalk user interface, we suggest that,for the moment, the following exercises be completed as paper exercises only.

1. Translate the following Pascal ex-pressions into Smalltalk (assume all Pascal variables are of type Integer).

a. units:= number mod 10 b. hundreds:= number div 100 c. tens:= (number mod 100) div 10 d. number:= (hundreds

*

100)+

(tens

*

10) +units

The binary selector /Iis the equiva-lent of the Pascal div operator.

Selector \\ is the equivalent of mod.

2 Translate the following Pascal frag-ments into Smalltalk (assume all Pascal variables are of type Integer).

a. IF value>5 AND value < 10

d. {Compute the smallest power of 2greater than a specified bound}

value := 2;

power := 1;

WHILE value <= bound DO BEGIN

value := value

*

2;

value := value

*

2;

Dans le document til Smalltalk (Page 69-83)