Dots Game
Volume Number: 2
Issue Number: 12
Column Tag: MacApp Objects
Dots Game Introduces MacApp 
By Larry Rosenstein, Product Engineer, MacApp, Apple Computer, Inc.
An Introduction to MacApp
Suppose you just got a great idea for a new Macintosh™ application. Regardless
of whether it deals with music, animation, or (even) word processing, one task you
face is developing the particular features of your program. After all, these features
are what you -- and it is hoped your users -- are most interested in seeing on the
screen.
On the Macintosh, users also judge applications by their interfaces. In order for
your program to be accepted, it should follow the user interface guidelines as closely as
possible. Unfortunately, the more time you spend implementing the user interface, the
less time you can devote to the exciting (musical, graphical, etc.) parts of the
application.
To help solve this problem, a group at Apple®, which included me, developed an
application framework called MacApp.™ MacApp automatically implements the
standard features of a Macintosh application, which allows you to concentrate on the
unique parts of your program.
For example, MacApp completely handles moving and resizing windows,
scrolling, printing, and displaying error alerts. MacApp also provides a general design
for other features such as Undo and document filing that make it much easier to
implement these features.
In this article I want to first describe the basics of object-oriented
programming, which helps make MacApp possible, and the overall MacApp
architecture. Then I will describe a small game program written using MacApp.
Object-Oriented Programming
It is difficult to explain object-oriented programming in only a few paragraphs.
Our experience with brand new MacApp programmers is that it takes a couple of weeks
for them to understand object-oriented programming and feel comfortable using it. So,
don’t worry if you don’t grasp the concepts right away. What is object-oriented
programming anyway?
Object-oriented programming is a way of writing programs. Conventional (that
is, non object-oriented) programs are organized around a set of data structures, and
procedures or functions that manipulate those data structures. For example,
MacDraw™ represents graphical shapes by a variant RECORD type in Pascal, and
implements routines to draw, stretch, save, etc. these records. Each of these routines
uses a CASE statement to distinguish the different kinds of shapes and perform the
appropriate action for each.
Fig. 1 Complete Mac Game in Two Days
Object-oriented programs, however, are organized around a set of object types.
Each object type defines both data structures, or fields, and methods that operate on the
fields. An object definition, therefore, is much like a RECORD definition in standard
Pascal.
The methods implemented for an object are the only routines that modify the
object’s fields. (Although Object Pascal does not enforce this restriction.) The process
of calling a method is often called message passing. The programmer creates a set of
objects and sends them messages. When an object receives a message, it decides what
action to take (i.e., what method to execute). Different objects can respond to the same
message in different ways.
Object-oriented programming also involves the concept of inheritance. This
means that one object type can be derived from an existing object type. A derived type
inherits all the behavior (fields and methods) of the base type. It can, however,
override any method it chooses.
For example, I could define a Rectangle object type that implements 2 methods:
Draw, which calls FrameRect to outline the rectangle, and ComputeArea, which returns
the rectangle’s area.
Later, someone else could define a subtype called ShadedRectangle that is derived
from my Rectangle object. ShadedRectangle would override the Draw method and call
FillRect with a particular pattern. Since the area of a shaded rectangle is the same as
that of an empty rectangle, the ShadedRectangle object type can inherit my
ComputeArea method. (In Figure 2, you see that both Rectangle and ShadedRectangle
share the implementation of ComputeArea.)
Because ShadedRectangle was derived from Rectangle, it inherits all the behavior
of Rectangle. Any piece of code that deals with Rectangle objects can also deal with
ShadedRectangle objects without change.
Figure 2.
If MacDraw were written using object-oriented programming, it might define a
generic Shape object type. Shape objects would define the methods that all shapes must
implement, for example Draw, Stretch, Save, etc. There would also be specific object
types, derived from Shape, such as Rectangle, Oval, Text, etc. (The Oval object type
could also be derived from the Rectangle type, since ovals and rectangles differ only in
the Quickdraw procedures used to draw them.)
The value of object-oriented programming is evident when you start modifying a
program. Suppose you wanted to add triangle objects to MacDraw. In a conventional
programming methodology, you would have to add a new variant to the MacDraw shape
RECORD type, and modify each of the routines that deal with shapes so that they handle
triangles.
With object-oriented programming, adding a triangle shape would involve
creating a Triangle object type, which would be derived from the generic Shape object
type, and implementing the methods Draw, Stretch, Save, etc. The main part of the
program, which just sends messages to shape objects, does not have to change at all.
Notice that all these changes are localized in the definition of Triangle, rather
than scattered throughout the program. Also, since the main part of the program does
not change at all, it is possible to add new kinds of shapes without recompiling the rest
of the application. In fact, you don’t even need the sources; you can simply link in the
modules containing the new object types.
MacApp Basics
MacApp consists of a set of object types. The MacApp objects themselves
implement most of the standard features of Macintosh applications, such as window
resizing. You can compile MacApp right out of the box and get a fully-functional
Macintosh application that doesn’t do anything.
To write your application you define object types that are derived from the
standard MacApp objects and override any methods you choose. To use MacApp, you
generally deal with the following five object types:
(1)TApplication, which handles the top-level control structure and main event
loop of your program. It also handles global commands (Open, Quit, etc.) and
creates TDocument objects.
(2)TDocument, which contains the data used in your program, and provides
methods for manipulating the data. It is also responsible for reading and writing
the data to disk, and creating TView and TWindow objects.
(3)TView, which draws your data on the screen, and processes mouse clicks.
(4)TWindow, which represents a Macintosh window that can move, resize, and
scroll.
(5)TCommand, which represents an undoable action in your program. It is
responsible for performing an action as well as undoing it.
When you write a program using MacApp, you generally define new object types
derived from TApplication, TDocument, TView, and TCommand. In most applications,
the standard TWindow type defined in MacApp is sufficient.
The best way to understand how MacApp works is to look at a sample program.
Rather than choosing one of the samples that comes with MacApp, I wrote a simple game
program from scratch. (This game took about 2 days to implement and debug.)
The game of Dots begins with a matrix of dots. Two players take turns drawing
horizontal or vertical lines to connect any two adjacent dots. Whenever a player draws
a line that completes a square, that player claims the square - in the paper version, by
writing his initials inside. The one with the most squares is the winner.
A screen shot of the game is shown in Figure 1. In my version of the game, the
players take turn clicking on a line with the mouse. Each player is represented by a
different pattern, and the program takes care of filling in squares with the appropriate
pattern as they are completed. It also keeps track of the score and inverts the score of
the player whose turn it is.
The first thing I did to implement the Dots program was think about the object
types I needed and the methods each needed to implement. Because of the MacApp
architecture, I knew immediately that I needed subtypes of TApplication, TDocument,
and TView (called TDotApplication, TDotDocument, and TDotView). To display the
current score, I needed another view object, called TScoreView. Finally, I needed a
TDotCommand object, so that a player’s move could be undoable.
Several of the methods I needed to implement were required by the architecture
of MacApp. For example, TDotView and TScoreView both needed a Draw method to draw
the game board and score respectively, and TDotDocument needed a DoRead method to
read saved games from the disk.
Each object type also needed methods specific to my particular application. For
example, TDotDocument implements methods for marking and erasing lines in the grid.
TDotView and TScoreView implement methods for invalidating parts of the views, which
are called by the TDotDocument.
Next, I needed a way to represent the state of the game and refer to lines and
boxes on the grid. I decided to name each line in the grid according to: (1) the dot at its
left or top end and (2) its orientation (horizontal or vertical). Whether or not the
line is drawn or erased is recorded in the fLine field of the TDotDocument, which is
simply a 3-level array of Booleans. Similarly, the state of each box is represented in
the fBoxes field of my document object.
The only methods that access these fields directly are in TDotDocument
(GetLineState, SetLineState, and ChangeBoxState). Hiding the representation of the grid
in this way allows me to easily change the implementation of TDotDocument without
affecting the rest of the program.
Once I defined the internal representation of the game, I then defined the
appearance of the game on the screen. This is done in the Draw method of TDotView.
The Draw method simply loops for each dot and calls another method, DrawCorner.
DrawCorner is responsible for drawing a dot, any lines leading from the dot to the right
or bottom, and the box to its bottom right. The important point to note here is that
DrawCorner uses the methods of TDotDocument to find out the state of the lines and box.
The other main function of TDotView is to handle mouse clicks. This is done by
its DoMouseCommand method. DoMouseCommand simply creates a TDotCommand object
and returns it to MacApp. (What happens to the commands object is described below.)
You will notice from the listing that TDotView also implements several methods
that deal with visual representation of the game. For, example, the method Pt2Line
converts a point within the view into a specification for the line at that point.
The TScoreView object provides a different view of the game. It simply
summarizes the current score and indicates whose turn it is. In MacApp, it is possible
to have several views of the same document.
TDotCommand serves 2 purposes. First, it tracks the mouse while the player is