In the example from Section 4.1, earlier in this chapter, we defined ScoreBox, a subclass of StaticText. Every message that ScoreBox can receive is defined either in itself or in its superclass (or in the superclass of that, and so on). The superclass defines some members; the subclass inherits these and adds some more. But in real life we may wish a subclass to replace the functionality of a member defined in its superclass. This is called overriding . To do this, one simply defines in the subclass a member with the same name as a message that the superclass can receive. Now the message is defined in both the subclass and the superclass.
In making an example, there’s no point using ScoreBox to
override any of StaticText’s members. This is basically the
same issue discussed in Section 3.6.4: where a
built-in class is concerned, to override a property would hide the
built-in functionality associated with that property, which is
undesirable, and to override a method is forbidden. So we need a
superclass and a subclass both of which we ourselves have defined.
Let’s subclass ScoreBox to make a
DoubleScoreBox class. This will be a
class that differs from ScoreBox only in that when it receives the
Increase message, it increments its score by
Create the DoubleScoreBox class and make it a subclass of ScoreBox. Clear the Window Editor of controls. Drag in a ScoreBox, select it, and give it a more meaningful name—TheScoreBox. Drag in a DoubleScoreBox and name it TheDoubleScoreBox. Drag in a PushButton and have its Action event handler go like this:
Now it’s time to define DoubleScoreBox’s Increase method, to override ScoreBox’s Increase method. Open DoubleScoreBox’s Code Editor and define a method handler Increase, with this code:
Sub increase() self.score = self.score + 2 // increment the score self.showScore // display the score End Sub
Run the project and press the button repeatedly. It works. Let’s talk about what’s happening.
It’s true that our instance TheDoubleScoreBox is a ScoreBox,
which defines an Increase method, to increment by
1; but its final class is DoubleScoreBox, which
also defines an Increase method, to increment by
2, and that’s the one that gets called when
we send the Increase message to TheDoubleScoreBox. On the other hand,
when TheDoubleScoreBox’s Increase method calls
self.showScore, DoubleScoreBox has no ShowScore
method; but the message is acceptable, because a DoubleScoreBox is
also a ScoreBox, and a ScoreBox does define a ShowScore method, which
is what gets called. We may summarize by saying that
names are resolved upward through the
instance’s class and its superclasses: first we look in the
final class of the instance to see if it accepts the message; only if
not do we look at its superclass, and so on. This is illustrated in
Our use of the phrase “the instance” may seem surprising, because in the previous section the important thing was the declared datatype of the reference. To understand what’s happening, separate the message-sending process into two distinct stages. First, REALbasic decides whether the reference can be sent the specified message at all; this is done by resolving the message name upward from the reference’s declared datatype. If it can, then REALbasic decides which class should actually receive the message; this is done by resolving the message name upward from the instance’s actual final class. For example:
dim s as scorebox s = theScoreBox s.increase s = theDoubleScoreBox s.increase
This increases TheScoreBox’s score by
TheDoubleScoreBox’s score by
2, even though
the reference both times is
s, which is declared
as a ScoreBox. Do you see why? The fact that the
is declared as a ScoreBox means that it can accept the Increase
message, so this code is legal—and
that’s all it means. Now we come to the question of what this
code will actually do; and that depends upon the
s points to. When
s points to an instance of the ScoreBox class, it
is ScoreBox’s Increase method that is called. When
s points to an instance of the DoubleScoreBox
class, it is DoubleScoreBox’s Increase method that is called.
The principle here is that all programmer-defined methods are virtual methods . That’s just technical talk for the very thing we’ve just been saying: the class of a reference may make it a legal recipient of a message, because that class can handle that message; but that fact is no guide as to what class will actually handle the message, because the instance may be of a class that overrides it.
Let’s look at the matter from a different perspective. Instead of a linear architecture, imagine a situation where one class has two subclasses. We’ll take advantage of overriding a virtual method so that it becomes, in effect, a decision-making mechanism. Suppose we’re writing a Tic-Tac-Toe game. Every game piece is either an X or an O. But all game pieces have some common behavior. For example, a game piece should know how to draw itself in a given square on the board. Clearly we’re going to have a GamePiece class and a Square class. We envision a routine that takes a piece and a square and tells that piece to draw itself into that square, like this:
Sub drawPieceIntoSquare(p as gamePiece, s as square) p.drawYourselfInto s End Sub
The exact behavior of
p is going to depend on
whether it is an X or an O. What will it mean, to be an X or an O?
Let’s make it mean that there is an X class and an O class!
Obviously, these are both subclasses of GamePiece. Presume that
p is either an X instance or an O instance. If the
X class has a DrawYourselfInto method and the O class has a
DrawYourselfInto method, then when we send
DrawYourselfInto message, the right thing will happen automatically.
p is an X instance, X’s DrawYourselfInto
will execute; if
p is an O instance, O’s
DrawYourselfInto will execute. What handler will execute depends upon
what class this particular instance is during this particular call to
our routine. So the class structure becomes a decision-making
mechanism! The fact that we can send a message to one higher class as
a way of choosing among several lower classes is called
However, we’ve left out a small piece of the puzzle. We define GamePiece. We define X as a subclass of GamePiece, and we give it a DrawYourselfInto method. We define Y as a subclass of GamePiece, and we give it a DrawYourselfInto method. We try to execute the previous code, and we get an error. What’s happened?
The answer is that we forgot the first part of the message-sending
process. We know that the DrawYourselfInto message will get routed to
the right class, the class of the actual instance that
p points to. But we also have to make it legal to
send the DrawYourselfInto message to
p in the
first place! This means that GamePiece must also have a
DrawYourselfInto method handler. In other words, in order to take
advantage of a virtual method, there has to be a
virtual method. You can’t override something that isn’t
there in the first place.
Very well; but what should GamePiece’s DrawYourselfInto handler
do? It’s perfectly possible that it will do nothing at all; it
might be completely empty! It could be that the whole of X’s
drawing functionality is contained in its DrawYourselfInto handler,
and the whole of O’s drawing functionality is contained in its
DrawYourselfInto handler. And every GamePiece is going to be an X or
an O. Our routine is never going to receive a value for
p whose final class is GamePiece; it will always
be an X or an O. So GamePiece’s DrawYourselfInto handler does
nothing, because it will never be called. Is this silly? Not at all.
In fact, it’s such a common technique that it has a name:
GamePiece’s DrawYourselfInto handler is said to be
It exists only so that X and O can override it, so that a reference
declared as GamePiece can be sent the DrawYourselfInto message,
taking advantage of polymorphism.
In fact, we can go further. It may be that no instance whose final class is GamePiece will ever be generated throughout the entirety of our program. Not just GamePiece’s DrawYourselfInto handler, but the whole GamePiece class, might exist only so that X and O can subclass it and take advantage of polymorphism. Again, this is quite common, and GamePiece is then said to be an abstract class . (See the Appendix A for a Tic-Tac-Toe game that employs this sort of architecture.)