September 92 - Rapid Prototyping With Handlers in AdLib
Rapid Prototyping With Handlers in AdLib
Nick Nallick
When you hear about the advantages of object-oriented programming, one of the
benefits that usually comes up is rapid prototyping. Rapid prototyping is the creation
of a trial application that looks like a commercial application without investing much
work in programming. This can be very useful when designing the feature set and user
interface of a new application.
The addition of behaviors to MacApp allows a new dimension in prototyping. A library
of generic behaviors can be created and added to an existing program to provide new
functions quickly without additional coding. AdLib carries this capability a step
further by providing a general purpose behavior called a Handler. Handlers provide
many simple functions in response to various messages within MacApp. Thus, many
useful functions can be easily added to a prototype by creating handlers along with the
views are created.
How rapid is your prototyping?
One of the reasons for the popularity of HyperCard is the ease with which you go from
creating a button to using it. Do you want to add a new function to a card? No problem,
just switch to edit mode, create a button, name it, and assign the function to it. Switch
back to run mode and you're ready to use the new function. Of course, this glosses over
the complexity of the new function itself, but many functions are relatively simple,
especially in the prototyping phase of development. For example, when I select this
menu item, I want that window to open, or when a particular button is pressed it must
signal the application or document.
MacApp has not always been the most convenient prototyping environment. To add the
same button as in the HyperCard example, you go to your view editor, create a button,
name it, and assign a new class name to it. Not too bad so far, but now you have to go
back to MPW, create the new class, override DoEvent(), provide the function, and run
through the compile, link, and rez cycle. And, by the way, don't forget to make sure
your class doesn't get dead stripped by the linker, or you'll have to run through the
build cycle again. If you're trying out a lot of small changes in a prototype, this can get
tedious.
The idea behind handlers in AdLib is to move this cycle as far into the view editor as
possible. This provides a faster, easier way to perform simple functions.
We work hard so you won't have to.
Handlers are general purpose behaviors that allow objects in an application, such as
views, to respond to specific messages by sending different messages to other objects
within the application. In other words, in response to a message, such as a mouse
command, a view's handler could send some message, such as an event number to the
view's document or some other object. Handlers can respond to and send a wide variety
of message types, and they provide a number of target objects to send the messages to.
Messages are handled in the same way as you might handle them in a MacApp
application by overriding such members as DoEvent() or DoMouseCommand() in your
objects. In response to a particular message, a handler performs some action. Actions
either send messages, such as events or menu commands, or perform simple functions,
such as opening a window or enabling a menu. Most actions are performed on a target
object. The target may be the application object, the document, the head of the target
chain, or some other object, such as a view specified by its identifier.
All of the handlers for an object are implemented by attaching another object of class
THandlerBehavior to it. THandlerBehavior is derived from TBehavior, so handlers can
take advantage of the behavior mechanism introduced in MacApp 3.0. Behaviors, if
you're not familiar with them already, are objects that can be attached to descendants
of TEventHandler in view resources or at run time. Behaviors get a chance to respond
to messages from MacApp before the objects they are attached to. A THandlerBehavior
object is stored in a view resource with additional data to indicate which messages to
respond to, in what ways. A single THandlerBehavior object can provide handlers for
any number of messages within an object. The source for the THandlerBehavior class
is included with AdLib, to provide for the most effective use in each application.
Since behaviors can be added to any class derived from TEventHandler, including
TApplication, handlers can be added to the application object itself. This provides a
convenient place to handle menu and other global functions in a prototype. For
example, in response to a Setup Menu message, an application's handler can enable all
of the appropriate menus. Furthermore, in response to particular menu command
messages, it can open the corresponding windows. This, in conjunction with MacApp's
default menu command handling, can provide a very lifelike prototype without any
high level coding at all.
Handlers are ideal for user interface elements that exist only to send a signal when
selected. One increasingly popular construct of this type is the tool bar-a strip at the
top of the document window containing small buttons that perform menu commands or
other useful functions. Handlers can be attached to these buttons to send the desired
menu command or perform other simple functions. Another useful concept is to design
objects that respond to custom events through their DoEvent() members. For example,
a text document might respond to a "go to the next page" event that could be sent by the
handler of an arrow button.
Pay no attention to the man behind the curtain.
Adding handlers to objects in AdLib is a straightforward operation accomplished by
point and click functions, without the need for scripting. To invoke the Handlers dialog,
select a single view, or to edit the application object, select the AdLib document
window, and choose Handlers from the Object menu (the dialog shown below appears).
The Handlers dialog displays a list of all handlers for the object. You can add, remove,
or change the priority of handlers in the list. Several handlers can exist for each
message type. They will be executed in the same order in which they appear in the list.
The right half of the window contains controls to configure a handler. The illustration
above shows a single handler for the Menu Command message. The handler will respond
to menu command #1000 by sending event #123 to the view with the identifier 'view'
in the window with the identifier 'wndw'.
A typical prototype application object will contain several handlers for the Setup
Menu message to enable menus, and handlers for each specific Menu Command message
to open windows and perform other functions. By handling the Initialize message an
application object can display a "splash screen" to occupy the user while the
application is starting. Functions in windows and dialogs can be provided by attaching
handlers to buttons and other controls within the windows. By clever use of handlers
in the application object and controls, in conjunction with the built in menu handling
functions in MacApp, you can create a prototype that comes remarkably close to the
real thing.
If you add handlers to your application object, you should derive your application
object from TBehaviorApp instead of from TApplication directly. This will ensure that
all the application's behaviors are loaded during initialization. As it is with
THandlerBehavior, the source for TBehaviorApp is included with AdLib.
A recyclable prototype
By editing an application after it has been built, we can change its functions by adding
to or changing its handlers. Since we can now add useful functions to an application
without rebuilding it, the next logical step is to build a generic application expressly
for the purpose of adding functions later. In other words, if we create an empty
MacApp "shell" application, we can add resources such as menus and views to the basic
shell and create working prototypes without touching MPW. One MacApp application
becomes the basis for any number of completely different prototypes. The only trick in
this application is to make sure that THandlerBehavior is included. This can be
accomplished by including it in the MAMake file and referencing
DontDeadStripHandler(). The following is the entire prototype shell program:
void main()
{
TBehaviorApp* application;
InitToolBox();
if (ValidateConfiguration(gConfiguration))
{
InitUMacApp(10); // call MoreMasters 10 times
InitUPrinting();
InitUTEView();
InitUDialog();
InitUGridView();
application = new TBehaviorApp;
application->IBehaviorApp('????', '????');
application->fLaunchWithNewDocument = FALSE;
// if we want this, we can do this with a handler
application->Run();
}
else
StdAlert(phUnsupportedConfiguration);
if (gDeadStripSuppression) // never TRUE
{
extern pascal void DontDeadStripHandler();
DontDeadStripHandler();
}
}
If you have written many MacApp programs you will probably recognize most of this
as standard code. We are using TBehaviorApp as our application object to load any
application handlers and telling it not to start with a new document, since we don't
really have a document class defined. The prototype can pretend to have a document by
opening a document window from an Initialize handler in the application object.
Beyond the prototype
Eventually, successful prototypes have to become working applications. It is usually
desirable to carry over as much of the prototype as possible to the final application.
Today there are a number of prototyping systems available that address this issue by
generating source code for a target environment. This has the advantage of working for
a number of development environments, but overall it is inferior to a good application
framework based approach. The basic problem is customization. As any MacApp
programmer can attest to, the default behavior of an element will eventually need
modification. If you have to modify the code generated by a prototyping system, it is
very difficult to return to the prototyping tools to make a change. You will have to
track the changes you have made to the source and put them back each time it changes.
On the other hand, changing the characteristics of an element in an object oriented
environment is a accomplished by overriding parts of the original. This has little
effect on the development tools.
The advantage of using handlers for prototyping then, is that their prototyping
capabilities are a side effect of their development capabilities, not the other way
around. We are beginning to see a number of new tools become available that will
change the way applications are structured. One significant new capability is dynamic
linking-the ability to add new classes and modules to an application while it is
running, rather than during the compile and link phase. MacApp now supports an early
version of an Apple tool called Dinker (for Dynamic Linker) that provides this ability.
With dynamic linking it is possible to create new libraries of objects to build
prototypes with more complex characteristics than what most of us are using today.
This may blur the line between prototyping and development as prototypes are
gradually enhanced to the point where they become the final application.
Conclusion
Handlers provide a powerful mechanism for performing simple functions without
creating special classes, and for linking views to other application elements. This
eases the requirements on the programmer for creating application prototypes as well
as the final applications. By building a MacApp shell application and editing copies of
it, you can create any number of useful prototypes without writing any code at all.
Furthermore, if you wish, the functions of your prototype can be used directly in your
final application with or without change. This is an advantage over prototyping
systems that generate source code because changing the generated source makes it very
difficult to return to the original editing environment.
Handlers provide a way to add simple functions to an existing application. As dynamic
linking becomes more accessible through tools like Dinker, complex functions will
also be eligible for addition in this way. This may completely change the way most
people develop application software in the future, but for now we can take one step in
that direction.