Reusable MacApp
Volume Number: 2
Issue Number: 12
Column Tag: Advanced Mac'ing
Reusable MacApp Classes 
By Kurt J. Schmucker, PPI, Inc.
Kurt Schmucker is Technical Director for Educational Services at PPI in Sandy
Hook, CT. He is also author of the best selling book Object-Oriented Programming for
the Macintosh. In this article, Kurt introduces us to some nifty object oriented
programming constructs for MacApp.
Designing Reusable Classes for MacApp Applications
One of the most important benefits of object-oriented programming is the ability
to easily reuse code. This reusability is of a completely different nature than the
reusability of the Mac Toolbox, on one hand, and a Pascal unit, on the other. This is
because of the dynamic binding and inheritance of object-oriented languages, of which
Object Pascal is one example. In an object-oriented language, the messages sent to
objects are not resolved until run-time and this resolution depends on the class of the
object that receives the message. This dynamic binding is essential to the design and
implementation of a framework like MacApp. For example, when MacApp wants to
draw the interior of one of the windows in your application, it sends the Draw message
to the view object installed in that window with some code like this:
window.fView.Draw;
The view that receives this message will be an instance of some view class that
you have designed, and since you overrode its Draw method, the code you wrote will be
invoked by this message. In this way even though the MacApp framework was written
independently from your application, it can still call your code.
In addition, this same message ( window.fView.Draw) is used for all the windows.
For each window, the Draw message is sent to the appropriate view object which
“knows” how to draw itself. Thus, the MacApp code for the TWindow class is reusable
across all MacApp applications since it can be used without change in all of them.
There is also another kind of reusability. You might want to reuse all the
functionality of the MacApp TWindow class, for example, except that you want one little
change: you want a window that always asks for a confirmation before opening or
closing. (Okay, so this is a weird thing to do. I'm just trying to make a point.) In an
object-oriented language like Object Pascal, this is easy to do. You just design a new
subclass of TWindow, call it TConfirmingWindow. This new class inherits all the
functionality of the TWindow class, but in addition, you can augment the actions taken
when the window opens or closes by designing your own methods for the Open and Close
messages. Designing your own methods for the TConfirmingWindow is considerably
easier than defining your own window or menu definition procedures -- the closest
analog on the Mac. For example, this would be the way you would augment the open
behavior of the window:
PROCEDURE TConfirmingWindow.Open; OVERRIDE;
BEGIN
IF SELF.ConfirmOpen
THEN INHERITED Open;
END;
Fig. 1 The QuadWorld Program
This method is invoked when an instance of TConfirmingWindow is sent the Open
message. This method first sends the message ConfirmOpen to this window, presumably
to ask the user if this window really should appear on the Mac screen. If the answer is
yes, this method then invokes its inherited Open method: the Open method for the
TWindow class.
In this last example, you have reused code by inheriting it, overriding to
augment the existing behavior of the window. The bottom line is that to implement the
confirming window took you just a few lines of code.
This article discusses how you can use dynamic binding and inheritance to build
your own reusable MacApp classes. In doing so, we will develop such a class and
demonstrate its reusability by subclassing it to achieve two new kinds of functionality
with only the minimum of work.
Note that the style of reusability possible in an object-oriented language is not
meant to replace the kinds of software packaging found in traditional subroutine
libraries like the Toolbox or a unit. Rather it is an additional tool in the hands of the
Mac programmer, a tool that is appropriate for many tasks but not everything.
Rules of Thumb for Reusable Classes
Using an object-oriented language does not guarantee reusable software, just as
using structured programming does not guarantee readable or maintainable code.
Object-oriented programming does, however, give you the potential for a level of
reusability unachievable in languages that do not have dynamic binding or inheritance.
Here are some of the rules of thumb that I use to design reusable classes. Like
all rules of thumb, they are meant to be followed in moderation.
Rule 1: Whenever you are faced with a choice, design a new method.
Rule 2: Methods should be single purpose.
Rule 3: Store as instance variables the data that is needed by more than one of your
methods, or data that may be needed by a subclass.
Rule 4: In deciding on the superclass for a new class, method inheritance is more
important than data inheritance.
Rule 5: Write for the library, not just for yourself.
Rule 6: Code defensively. You never know who will call your methods and under
what circumstances.
Rule 1 encourages all major and most minor decisions to be made in separate
methods so that these decisions can be overridden in subclasses. This gives the class the
maximum flexibility as a base for new subclasses. Because this principle was followed
in MacApp, it was easy to design the TConfirmingWindow class. What should happen
when a window is to be open is a single method in the class and thus to change this in the
subclass, only one method need be overridden. There is one drawback to following this
rule: proliferation of methods. You can see this in MacApp, which consists of only 39
classes but over 560 methods. The benefits of this rule, however, outweigh this
inconvenience.
Rule 2 reiterates that methods should be replaceable components of building
blocks: replaceable in subclasses. If methods are multi-purpose, this is difficult. In
the case of the TConfirmingWindow class, we were able to override one decision --
what to do when a window is to be opened -- in one method, without having to worry
about what other things this method might do. This made the TConfirmingWindow class
an excellent base for new classes. A corollary of this rule is that methods tend to be
short: at most a few dozen statements or so.
Rule 3 encourages you to avoid global variables by storing data needed by several
methods in instance variables. In addition to the data you need, consider also the data
that might be needed by a subclass: data needed to support different resolutions of the
decisions you made. (Rule 1)
Rule 4 helps you decide on the superclass for a class you are designing. Examine
the behavior you will inherit from the superclass rather than the data you will also
inherit.
Rule 5 encourages you to go that extra step or two. Don’t do just the bare
minimum that will solve the problem at hand: design the additional functionality that
will make this a class worthy of being in a library.
Rule 6 warns you that since your class may be subclassed by many other
programmers, its methods may be called in circumstances you cannot control. You
must therefore code extremely defensively.
QuadWorld Geometric Editor and the TListView Class
The application that will be used to demonstrate this design of reusable classes is
the QuadWorld application. QuadWorld is a simple MacDraw-like geometric editor. It
allows the user to enter and manipulate various kinds of quadrilaterals without regard
to the geometric constraints associated with special quadrilaterals like rhombi.
QuadWorld presents two representations (views) of the quadrilaterals to the end user: a
graphical view and a textual view. It is the textual view that will be developed as a
reusable MacApp class. Figure 1 is a screen dump of QuadWorld in action. For a more
detailed discussion of the implementation of the QuadWorld application, as well as a
complete listing of the QuadWorld application, see Object-Oriented Programming for
the Macintosh, Hayden, 1986, by myself. The complete source is also available on the
MacTutor Source Code Disk for this issue, Number 15, $8.00 from MacTutor.)
The list view class is a good candidate for some special design attention since its
functionality -- displaying a list of text items so that the user can select one with the
mouse -- is needed by many applications. (QuadWorld was designed and implemented
before the new 128K ROMs with the List Manager was released.) Other parts of
QuadWorld are not such good candidates. After all, how often do you need the code to
rotate a rhombus?
This brings up an interesting point. Any class designed in Object Pascal can be
reused and so, for that matter, can any piece of code. What we are talking about here is
designing the code of a class to be used with the MacApp class library, so that it can be
reused easily by other programmers who may not have access to the source code or
don’t want to bother with all the fine points of the algorithms involved. These
programmers will still want to be able to make small modifications to the functionality
of the class, in the way that TConfirmingWindow is a slight modification of the vanilla
window modelled by TWindow. This can be almost trivial to do, if the original class was
designed well, by following the rules of thumb discussed earlier.
The TListView Class
The essential structure of the TListView class is shown in Figure 2. This
collection of methods follows most (if not all) of the rules outlined above. For example,
since I had to make a choice when setting up the QuickDraw port (font, text size, text
style, etc.), I coded my preference in a separate method (SetPen). If you needed the
functionality of this class, but wanted one of these characteristics to be different, you
would only have to subclass TListView and override this one method.
Less obviously, whenever I am about to do something with an entire entry in the
list (test for coverage by a mouse click, highlight, or move the pen into position for
drawing), I use a method to compute the rectangle surrounding an item. Who knows,
perhaps someone would want to accomplish this aspect of a list view differently. (In
fact, the last section of this article does exactly this.)
In the area of data, the fCurrentItem instance variable always stores the number
of the item currently being processed (drawn, enumerated, etc.) in case a subclass
needs to use it. The fItemHeight variable stores the height of an individual item since
this data is needed by several methods.
Of course, like any good idea, you can carry these rules-of-thumb of class design
too far. Abusing rule 1, for example, will result in so many methods that no one can
understand what is going on and, in addition, performance will degrade. I hope that I
have successfully straddled the fence between a class that can’t easily be used as a basis
for inheritance and one with so many methods as to be incomprehensible.
When a class is properly designed, note how short and simple the methods turn
out:
PROCEDURE TListView.ChangeSelection(
index: INTEGER);
BEGIN
SELF.fFrame.Focus;
SELF.DoHighlightSelection(SELF.fHLDesired, hlOff);
SELF.fCurrentSelection := index;
SELF.DoHighlightSelection(hlOff, SELF.fHLDesired);
END;
FUNCTION TListView.DoMouseCommand(