TOM
 
The target/action paradigm
 
 
TOM Home
TOM Tasks

FAQ
News
Highlights
Publications
Documentation
Download TOM
TOM Software
Bug Database
Mailing Lists

Mail:
tiggr at gerbil.org

Short Cuts:
Tesla
TOM/Gtk
GP
MU

Snapshots:
all of 'em
tom [an error occurred while processing this directive]
tesla [an error occurred while processing this directive]
mu [an error occurred while processing this directive]
tomgtk [an error occurred while processing this directive]

Released:
all of 'em
tom 1.1.1
tomgtk 0.11
tesla 0.91
gp 0.5
mu 1.0

Misc:
GIF free NOW!

The target/action paradigm is about interacting with the control elements in a graphical user interface (GUI). The target/action paradigm isn't a TOM topic per se; it is already thoroughly used in NextStep/OpenStep (I don't know of any prior art, but I can imagine it exists). However, if I explain it now, I can build on it in future TOM highlights, since in TOM we can do things with it that Objective-C, which is the language of choice for OpenStep, lacks.

The target/action paradigm is a way of dispatching GUI events. If we first consider prior art, there are several ways by which events are usually dispatched. We will skip inflexible non-OO solutions like call-back function pointers with (or without) a `userData' argument. In the text following, we will implicitly focus on how a user click on a button is handled.

Prior art

A popular solution is for the button to invoke a method on itself, e.g., isClicked. If the button is to do anything more than nothing (the default), the button should actually be an instance of a subclass of the Button. This subclass will implement the isClicked method and do whatever is necessary. This method can provide the actual functionality itself, or invoke a method of some object that implements that functionality. The result is that either the brains behind, for instance, some dialog box are scattered throughout the code of the program, or there are lots of classes that just exist to forward an event of one GUI element to a object that packs the functionality for that dialog box. Net effect is that you need a huge number of classes, one for each button, knob, or other GUI element: lots of code exists that doesn't do anything; it is just there to get something done---code bloat of the worst kind.

One solution to this problem is to not invoke the isClicked of the button itself, but on some object that will be the target of the button. That target must be of a subclass of some ButtonTarget class (and override the virtual isClicked method, in C++ speak). However, this only shifts the problem from numerous Button classes to numerous ButtonTarget classes. The solution is seemingly trivial: the button passes itself as an argument to the isClicked method. The method can then discern between different buttons and perform the desired action depending on the button sending the event. This has two disadvantages: the method must decide what to do based on the sender, which costs time, and the method must be able to discern the buttons, which costs space. (Discerning the buttons means that either the buttons must be known, so reference comparison can be used to decide which button it is, or each button must store a tag, so the tag of the sender can be compared with the known tags.)

A solution to this second problem was thought of by the developers of Java, who added the concept of inner classes to the language. Now for each button target, you just define an inner class that inherits the ButtonTarget and (only) implements isClicked. Syntactically, this class is part of the outer class. Semantically, the instance of the inner class has direct access to the methods and instance variables of the outer instance. Aesthetically, it is a hack that complicates the language and, incidentally, doesn't solve the too-many-classes problem.

Target/action

The target/action paradigm is a simple solution to a basically simple problem. The problem with the existing solutions is that it is always the same method that is invoked, irrespective of whether the button itself or its target are to implement that method. If the method to be invoked can be specified, the problem is solved.

In a previous highlight (15 Feb 1998, The selector type), the selector type has been introduced: a selector is a name for behaviour that is not bound to a particular object. (The selector type is present in languages like Smalltalk, Objective-C, and TOM; it is lacking in, e.g., Java, Eiffel, and C++.) With the target/action paradigm, each button knows its target, which is the object to be informed of the user clicking the button, and the action, which is the selector of the method to be invoked on that target. Now the action can be okClicked for the `OK' button, changedMyMind for the `Cancel' button, theUserDoesNotKnowWhatThisButtonDoes for the `Apply' button, or any method you can think of. More importantly, it can also be any method you, as the designer and implementor of the button, can't think of.

With the target/action paradigm, the target does not need to spend memory space to references to all its buttons (though each button spends space on the reference to its target). There are GUI elements, however, that contain more information than just having been clicked. For instance, lots of elements contain a current value (numeric text fields, slider, volume knobs, etc). The target must be able to retrieve that value. For this reason, every action method has one argument: the GUI element that triggered the invocation, normally called the sender. Thus, the code used by a button to inform its target, given its target and action instance variables, becomes:

[target perform action with self];

Another interesting application of the target/action paradigm is how it can interact with the responder chain. In OpenStep (and TAG), the GUI elements in a window are arranged in a tree: for example, a box contains a few buttons, another box some fields, etc., and the boxes are contained in a big box, which is part of the window. When the application is active, one of these elements will receive the key events. This element is called the first responder. Its parent in the hierarchy is the next responder, etc. Transitively, the first responder and its ancestors make up the responder chain, together with some global entities as the menu and Application object.

The first responder is set by a mouse click; the responder chain is used not only to dispatch key pressed events, but also to respond to the user activating for instance the `Cut', `Copy', and `Paste' menu items. How does this work? You don't want to have to fix the targets of these menu items when the first responder is changed. The solution is as elegant as it is simple: If the target of a GUI element is set to nil, when the action is to be performed, it is sent up the responder chain. The responder that knows how to handle it will handle it. It the message passes all the way through the responder chain without having been handled, the program will beep to indicate failure.

The piece of code shown above to inform the target is of course invalid given this responder paradigm (a nil-receiver condition will be raised). The actual code tells the shared Application object to dispatch the message. If the target is nil, it will be dispatched to the responder chain, otherwise the target will get the action message. (The action message must of course not be the null selector.)

[[Application shared] sendAction action
                      to target
                      from self];
This shows the flexibility of the target/action paradigm, and remember, it is only possible because the selector provides a name for behaviour that is not already bound to a particular receiver.

TOM?

So far, the discussion on target/action has been conducted in a way that applies to both Objective-C/OpenStep and TOM/TAG. In a future highlight, we'll discuss how in TOM curried Invocation objects can be used to provide extra flexibility by passing more than one argument in an action method to a target. Without having to modify the Button class, of course.
Up: Highlights
 
Copyright © 1997-2002 Programmers Without Deadlines