Software-Dokumentation objektorientierter Frameworks mit Entwurfsmuster
Referat von Jost Wiezorek
Quelle: "Documenting Frameworks using Patterns" von
Ralph E. Johnson
1. Einleitung
3.1 Beschreibung der Absicht eines Framework
3.2 Beschreibung, wie ein Framework benutzt wird
3.3 Beschreibung des Enturfs eines Framework
6. Rückblick
![]()
Was ist ein Framework?
Ein Framework ist ein kaufbares und ausführbares Programm. Es besteht aus einer Anzahl von Klassen ( keine Klassenbibliothek), die ein abstraktes Design für Lösungen zu einer Familie verwandter Probleme enthalten. Ein Framework stellt also einen wiederverwendbaren Entwurf dar.
Der Unterschied zu den Entwurfsmustern liegt darin, daß ein Framework auch den benötigten Code zur Verfügung stellt. Diesen Code kann man durch Manipulation auf die jeweils benötigten Lösungen anpassen. Entwurfsmuster sind nicht ausführbar, d.h. sie stellen nur die Methoden zur Lösung eines Teilproblems dar.
Die Dokumentation für ein Framework streift mehrere Gebiete und muß mehreren Bedürfnissen genügen. Diese Bedürfnisse können alle abgedeckt werden, wenn man eine Strukturierung der Dokumentation mit Hilfe eines Satzes von Pattern, auch Pattern Language genannt, verwendet.
Pattern können die Absicht (Sinn) eines Framework beschreiben, womit die Applikations-programmierer das Framework verwenden können, ohne daß sie detaillierte Kenntnisse über die Arbeitsweise des Framework besitzen.
Dieser Bericht zeigt, wie man Pattern zur Dokumentation eines Framework benutzt bzw. einsetzt.
Christopher Alexander, ein Architekt, entwickelte die Idee einer "Pattern Language", um die Menschen zum Entwurf ihrer eigenen Häuser zu animieren.
Eine Pattern Language besteht aus einem Satz von Mustern, welches jedes eine Lösung für ein Teilproblem darstellt.
Christoph Alexanders Pattern Language geht vom groben bis zum kleinsten Detail. Er fängt z.B. mit dem Grundriß des Hauses an und hört bei der Wahl der Farbe für die Wände auf.
Seine Pattern Language verspricht ein Dokument zu sein, welches Nichtarchitekten erlaubt ihr eigenes Haus zu entwerfen, ohne ein spezielles Training für die Benutzung der Pattern Language zu absolvieren.
![]()
Einer der wichtigsten Pluspunkte von Alexanders Pattern Language ist ihre Struktur. Jedes Muster ist in einem genauen Format geschrieben und sie werden so arrangiert, daß jedes Muster einen Übergang in sein Folgemuster bereitstellt. Das Format, das Alexander benutzt, ist für Software gut geeignet.
Zum Beispiel: Um physikalische Objekte zu bauen, benutzt Alexander zwei Bilder: eins, das für die Darstellung des zu lösende Problems zuständig ist und eins, das die Lösung darstellt.
Die Muster, die in diesem Bericht dargestellt werden, haben ein anderes Format, als die von Alexander.
Jedes Muster beschreibt ein immer wieder auftretendes Problem eines Framework und dann beschreibt es wie man das Problem löst. Alle Muster haben das gleiche Format.
Es wird folgendes Format benutzt:
Der Anhang beschreibt einen Satz von 10 Mustern für HotDraw, ein Framework zur Erstellung von Zeichen-Editoren.
![]()
Eine Dokumentation sollte drei Erwartungen erfüllen, wobei die Pattern (Muster) helfen können.
Ein Dokument sollte folgende drei Dinge beschreiben:
Muster eigen sich besonders, um zu zeigen, wie man ein Framework benutzt. Ein Satz von Mustern kann natürlich alle drei Bereich der Framework-Dokumentation berühren.
![]()
Ein Framework ist ein wiederverwendbarer Entwurf, der ein Problem löst, indem es das Problem in kleine Teilprobleme aufteilt.
Die Absicht des Framework, bzw. das Problemgebiet, für das es verwendet wird, muß das erste Thema sein, welches die Dokumentation behandelt.
Wenn der Benutzer am Anfang der Dokumentation schon feststellt, daß das Framework nicht auf sein Problem zutrifft, braucht er nicht mehr weiter zu lesen.
Der erste Abschnitt ist kurz und knapp gehalten, so daß man sich sehr schnell einen Überblick über das behandelte Problem verschaffen kann.
Es ist nicht unvernünftig anzunehmen, daß ein erfahrener Entwerfer alle Problembeschreibungen des Framework gelesen hat. Das reduziert die Suche nach der richtigen Software für ein gegebenes Problem.
Jedes Muster beschreibt das Problem, dessen Lösung es bereit stellt. Das erste Muster für ein Framework beschreibt sein Anwendungsgebiet. Dies geschieht oft anhand von Beispielen.
Zusammenfassend kann man sagen, daß das erste Muster den Rest der Muster in die Sprache einführt und es legt fest, welches Muster als nächstes gelesen wird.
![]()
Als nächstes wird dokumentiert, wie ein Framework verwendet wird, um eine Anwendung zu erstellen.
Die meisten Anwender eines Framework wollen nur so wenig wie nötig über das Werkzeug wissen. Das Bedeutet, sie wollen nicht wissen, wie der interne Aufbau eines Framework aussieht, sondern wollen eine genaue Unterweisung über die Benutzung des Framework haben, ähnlich wie bei einem Kochbuch.
In einem Kochbuch wird als erstes das Menü und die Zutaten beschrieben und danach folgt eine genaue Anweisung, wie die Zutaten verarbeitet werden. In einem Kochbuch steht aber nicht, welche Auswirkungen die einzelnen Zutaten bzw. Gewürze haben. Die Angaben in einem Kochbuch reichen für den normale Anwender aus, um ein Gericht zu kochen.
Die meisten Dokumentationen beschreiben als erstes, wie das Framework arbeitet und dann wird beschrieben, wie das Framework benutzt wird. Wie auch immer, niemand versteht ein Framework, ohne das er es benutzt hat, so daß das Anwenden wichtiger ist als die reine Theorie. Also ist es unerläßlich zu erklären, wie man ein Framework benutzt ohne Bezug auf die Arbeitsweise zu nehmen.
![]()
Die dritte Aufgabe der Dokumentation ist die Beschreibung des Entwurfs eines Framework. Dies beinhaltet nicht nur die verschiedenen Klassen in einem Framework, sondern auch, wie die Instanzen der Klassen zusammenarbeiten.
Obgleich Programmierer Objekte zusammenschalten können, ohne die genauen Funktionen zu kennen, ist ein Framework, welches im Detail verstanden wird, am wertvollsten.
Die größte Schwäche von "Kochbüchern" ist, daß sie nur den typischen Weg beschreiben, wie man ein Framework benutzt. Ein gutes Framework aber wird auf Wegen benutzt, die der Designer nicht für möglich gehalten hat, ähnlich wie beim Kochen das Würzen der Speise.
Also ist ein "Kochbuch" nicht geeignet, um alle Anwendungsmöglichkeiten eines Framework zu beschreiben.
Muster sind informelle technische Beschreibungen mit dem Ziel, die Benutzung eines Framework zu beschreiben und nicht ihre Algorithmen oder das Zusammenspiel von Mustern.
Muster versprechen, eine gute Möglichkeit für die Beschreibung eines Entwurfs zu sein und es ist möglich, viel von dem Entwurf eines Framework in ein Muster hineinzupacken.
Wo werden in einem Satz von Mustern die Entwurfsinformationen plaziert?
Generell werden die detaillierten Entwurfsinformationen solange wie möglich vom Leser ferngehalten, weil sich die meisten Lesern für diese Informationen nicht interessieren. Auf der anderen Seite, ist es erforderlich,daß die Muster diese Informationen irgendwo bereitstellen und daß diese Informationen nicht doppelt vorkommen.
Die Technik, die hier verwendet wird, behandelt einen Satz von Mustern als einen gerichteten Graphen. Die Kanten stellen die Beziehungen von einem Muster zum anderen Muster dar. Die Entwurfsinformationen werden soweit entfernt wie möglich von dem ersten Muster plaziert.
Einige Entwurfsinformationen, so wie die Tatsache, daß HotDraw Anwendungen wie Figuren, Zeichnungen und Werkzeuge herstellen kann, müssen in den ersten Mustern beschrieben werden. Die meisten Informationen aber können in untergeordnete Mustern, die keine Querverweise zu vorherigen Mustern besitzen, verschoben werden.
![]()
Beispiele spielen eine Schlüsselrolle in der Dokumentation von Frameworks. Die meisten Dokumentationen beinhalten eine Anzahl von Beispielen. Die Beispiele konkretisieren die Frameworks, machen den Ablauf verständlicher und helfen dem Leser zu entscheiden, ob man den Rest der Dokumentation verstanden hat.
Beispiele sind auch bei Dokumentationen von Mustern wichtig. Das Problem und die Lösung werden oft anhand von Beispielen erklärt. Es ist sehr schwierig, manche Probleme verbal oder schriftlich darzustellen, oft sind sie anhand von Beispielen wesentlich einfacher darzustellen.
Diese Beispiele zeigen nicht wie das Framework arbeitet, sie erklären auch nicht den Entwurf des Framework, aber sie zeigen wofür man das Framework benutzen kann. Beispiele leuchten die Vorzüge, die ein Framework auf der Benutzer-Ebene verspricht, aus.
Desweiteren können sie auch benutzt werden, um die Highlights eines Entwurfs zu verdeutlichen.
Die Muster von HotDraw verwenden Beispiele , um die Notwendigkeit von komplexen Figuren und Beziehungen zu unterstreichen. Diese Muster beginnen mit einer Beispielanwendung von HotDraw und enden mit einer Diskussion über den notwendigen Code, um diese Anwendungen zu implementieren.
Beispiele können für Testdurchläufe von Mustern verwendet werden. Sie führen alle Muster aus, so daß man sicherstellen kann, daß man sie alle kennengelernt hat. Die Beispiele, die zur Beschreibung der Absichten von HotDraw verwendet werden, reichen für die Testdurchläufe aller Pattern aus.
![]()
Der größte Teil dieses Artikels besteht aus einem Anhang, der Beispiele von Mustern eines Framework beinhaltet. Das Framework heißt HotDraw, welches ein Framework für einen symbolischen Grafikeditor darstellt, der ursprünglich bei Tektronix von Kent Beck und Ward Cunninghan entwickelt worden ist. Er wurde mehrmals weiterentwickelt. Die letzte Version, die von den Mustern beschrieben wird, wurde von Patrick McClaughry für ParcPlace ObjectWorks in Smalltalk 80 release 4.0 geschrieben. Diese Implementation war einfacher als die Vorgänger, weil der Entwurf von dem User Interface Framework von Smalltalk 80 release 4.0 von HotDraw beeinflußt wurde. HotDraw ist einfach und nicht so leistungsstark wie andere Grafik Editoren, aber gerade deswegen eignet er sich gut als Beispiel für diesen Artikel.
Das erste Muster beschreibt die Absicht von HotDraw. Die Muster formen einen gerichteten Graphen, mit dem ersten Muster als Wurzel. Die anderen Muster sind so um das erste Muster angeordnet, daß die am meisten benutzten Muster zuerst kommen.
Das zweite Muster z.B. beschreibt, wie die Unterklassen von Figuren erstellt werden. Diese Funktion muß jeder Benutzer von HotDraw kennen.
Das dritte Muster beschreibt unter anderem, wie man "new kinds of handle" herstellt, was normalerweise unwichtig ist.
![]()
Der Satz der Muster hat seinen Ursprung in dem Workshop "Towards an Architecture Handbook", welcher von Bruce Anderson 1991 organisiert wurde. Kent Beck und Ralph E. Johnson teilten das Interesse an HotDraw und Alexanders "Pattern Language", so daß sie die Gelegenheit nutzten, um ein paar Muster für HotDraw zu schreiben. Später gab Ralph E. Johnson diese Muster an Studenten, die HotDraw lernen wollten, weiter. Er stellte fest, daß diese Muster nicht hilfreich beim Erlernen von HotDraw waren. Zu dieser Zeit war jedes Muster nur ein Paragraph und beides, Beispiele und die Theorie von Entwurf waren von den Mustern abgetrennt.
Ralph E. Johnson studierte Alexanders "Pattern Language" erneut und realisierte, daß jedes Muster sich auf Beispiele abstützt und daß er seine Muster mit zugrundeliegender Theorie versehen hatte.
Als Ralph E. Johnson Beispiele in seine Muster integrierte, stellte er fest, daß ein zugrundeliegendes Beispiel nicht alle Muster illustrierte, so daß er ein oder zwei weitere Beispiele hinzufügte. Er packte genügend Theorie vom Entwurf in das erste Muster, um das Hauptvokabular zu definieren. Den Rest der Theorie packte er in die anderen Muster mit der Prämisse: so spät wie möglich, aber so früh wie nötig.
Die nächste Gruppe von Studenten, die die Muster gelesen haben, kannten Smalltalk gut, aber die meisten kannten HotDraw nicht. Ihr Feedback brachte nur noch einige Änderungen in den Mustern, aber nach ein paar Iterationen waren diese durchgeführt.
Diese Muster sind die einzigen Dokumentationen für eine Version von HotDraw, die seit Anfang 1992 vertrieben wird und die Benutzer sind mit dieser Art der Dokumentation zufrieden. Es ist sehr schwer, die Muster, die Schwächen zeigen, noch zu verbessern.
Nachdem Ralph E. Johnson das Format für die Muster festgelegt hatte, war das Schreiben der Mustern kein Problem mehr. Gute Beispiele zu finden ist hingegen nie einfach. Das schwierigste beim Schreiben eines Satzes von Mustern für ein Framework ist, dieses Framework so genau zu kennen, daß man weiß, wie jedes Feature verwendet wird.
Die Struktur der Muster helfen, sie zu entwerfen. Die Vorstellung, daß Muster gerichtete Graphen sind, erleichtert die Entscheidung, wo man Beispiele und Entwurfsinformationen plazieren kann und macht die Analyse auf Vollständigkeit der Muster einfacher.
Der Entwurf von Mustern für HotDraw beinhaltet eine Menge Arbeit, aber es hilft den Entwurf von dem Framework zu verstehen. Der Entwurf von Mustern beinhaltet die Analyse, wie die Benutzer die Handhabung des Framework erwarten.
![]()
Muster sind ein Weg, um Frameworks zu beschreiben, weil die Erstbenutzer meistens nicht exakt wissen wollen, wie das Framework arbeitet, sondern sind nur an der Lösung eines Teilproblems interessiert. Solange die Muster leistungsfähig genug sind, um die meisten Verwendungen des Framework zu beschreiben, werden sie dem Benutzer genügen.
Ein Satz von Mustern wird als Dokumentation für ein Framework für die meisten Framework-Benutzer ausreichend sein.
Nur Benutzer, die sich außerhalb der normalen Verwendung von Frameworks bewegen und detailliert wissen wollen, wie das Framework arbeitet, müssen die traditionelle Entwurfsdokumentation lesen.
Dieser Bericht ist nur ein erster Schritt zur Entwicklung einer Methode für die Dokumentation von Frameworks. In Zukunft werden Muster versuchen, die Dokumentation eines Frameworks auf andere Wege zu lösen, um zu sehen, welches Muster der Benutzer bei der Herstellung von Anwendungen am besten unterstützt.
Die Muster von HotDraw sind nur ein erster Schritt, der eine attraktive Möglichkeit für das Lösen des Problems der Framework-Dokumentation darstellt.
Eine andere Alternative sind die Muster von
Daimler Benz. Sie stellen interaktive Dokumentation von HotDraw dar.![]()
HotDraw is a framework for structured drawing editors. It can be used to build editors for specialized twodimensional drawings such as schematic diagrams, blueprints, music, or program designs. The elements of these drawings can have constraints between them, they can react to commands by the user, and they can be animated. The editors can be a complete application, or they can be a small part of a larger system.

Figure 1 PERT chart editor
Many programs need to edit twodimensional drawings such as schematic diagrams or blueprints. Sometimes the elements of these drawing can be treated independently, but often the elements have constraints between them. For example, Figure 1 shows a PERT chart consisting of rectangles representing events and arrows between the rectangles representing dependencies between events. Events display duration and ending dates, and have starting dates that are not displayed. The starting date of an event is the maximum of the ending dates of all the events that precede it, i.e. that have a connection to it, and the ending date of an event is the sum of its starting date and its duration. Changing the duration of one event will cause its ending date to change. This may cause the starting dates of other events to change, and so the ending dates, as well.
Direct manipulation techniques are usually the best way to edit a drawing. A user will manipulate an element of a drawing by using the mouse to select the element and then operating upon it. For example, when an event in the PERT chart is selected (e.g. the leftmost eventrectangle in Figure 1), it presents a set of handles. Dragging the corner handles will change the size of the event's rectangle, and selecting the handle on its right side will create an arrow that can be dropped on another event to indicate that the first event must precede the second. Moving an event or changing its size will cause the arrows connected to that event to move, too.
There are many ways to operate upon an element. One way is to move a handle that controls an attribute of the element. For example, events could have handles that control durations, and the duration would then be changed by moving the handle. A second way is to pop up a menu of operations on the element and select one. Events could have a "change duration'' menu item, which would prompt for a new duration. A third way is to choose a specialized tool from the palette to change the element's attribute. For example, the text tool can edit text, so a good way to change the duration and title of an event is to edit them with the text tool. In the case of the PERT chart editor, the text tool is the best way to change the duration, but handles are the best way to change the size of the display of an event, and menus are the best way to cut and paste events. The designer of a drawing editor needs a wide range of options, but must pick the most appropriate ones to keep from confusing the user.
The palette on the left of the editor in Figure 1 offers four tools: a selection tool (which is currently active), a scrolling tool, a text tool, and a tool for creating new events. Selecting a tool with the mouse makes it active.
HotDraw is a framework for semantic drawing editors (i.e. editors for drawings whose elements have constraints on their behavior) that was used to build the PERT chart editor of Figure 1. The most important class in HotDraw is Figure, which is the class of drawing elements. Figures are responsible for rendering, hittesting, and notifying dependents when their appearance changes. Other important classes are Drawing (which represents the entire drawing), Tool, Handle, and DrawingEditor.
Drawing editors created with HotDraw can be part of a larger application. For example, Figure 2 shows a network editor. The top pane in the editor is a HotDraw drawing of a network. The left two panes on the bottom select two of the nodes, while the bottom right pane lets the user display and change a weight on the edge between the two nodes. The network is drawn so that nodes are close together if they have a lot of communication between them, i.e. if their edge has a large weight. Adding and deleting nodes and edges is done using the three lower panes, but the user can rearrange (within limits) the nodes in the drawing directly. Note that the network editor has no palette. All the user can do with the drawing directly is drag nodes.

Figure 2 A Network Editor
The network editor is also an example of how drawings can be animated. It treats the graph layout problem as a variation of the nbody problem, and solves it iteratively. Each node has a repulsive force on every other node, but edges act like springs to keep nodes together. The drawing is animated by showing intermediate steps in the nbody solution. This lets the user rearrange nodes if the network is in a suboptimal, but stable, configuration.
DrawingEditor new open will create the drawing editor shown in Figure 3, which is the default version of HotDraw. This is what you will get if you don't redefine the tools that the drawing editor has in its palette.
Figure 3 shows the default drawing editor. Most of the tools in the palette on the left create different kinds of figures. The exceptions are the first two, which are the selection tool and the scrolling tool, the third and fourth, which rearrange the order of figures by bringing a figure to the front or to the back, and the fifth, which deletes figures. It is probably a good strategy to play with this editor for a while to learn the user interface. The menu on selected figures provides commands to cut and paste them, but all other changes to figures are performed by manipulating their handles.

Figure 3: Default drawing editor
To design a drawing editor using HotDraw, first list the atomic elements of the diagram. Each one will be a subclass of Figure. Decide whether the drawing needs to be animated. List the tools that will be in the palette and make a subclass of DrawingEditor with a tools method that returns an array of the tools making up the palette.
To create a subclass of Figure, see Defining drawing elements (2). To animate a drawing, see Animating drawings (9). If the drawing editor is going to be part of a larger tool, see Embedding a drawing in another tool (7). To put a tool in the palette, see Tools(8).
There are an infinite variety of primitive figures that can be included in a drawing. Thus, there needs to be a way to make new figures for each application.
Each kind of drawing element is a subclass of Figure. Note that there are already subclasses of Figure for the simple geometric objects, such as EllipseFigure, RectangleFigure, and LineFigure. Class CompositeFigure is the superclass of complex figures liked PERTEvents, which can contain other figures as components. Sometimes these classes are suitable to be used directly. For example, the lines connecting events in the PERT chart are just instances of LineFigure. Often it is necessary to make a subclass of Figure or one of its existing subclasses.
Most figures will be either CompositeFigures or subclasses of existing geometric figures like RectangleFigure or LineFigure, so they will inherit the visual presentation of their superclass. A subclass of Figure that is not a subclass of any other subclass must define its own visual presentation. Figure is a subclass of VisualComponent, so it must define displayOn:. It must also define origin, extent, and translateBy:. These are the only methods necessary to render and move a figure, and are the minimum methods necessary to define a new subclass of Figure.
The main distinction between Figure and other subclasses of VisualComponent is that a figure keeps track of other objects that depend on it. This has no effect on methods that do not change the state of a figure, such as origin, extent, and displayOn:. However, methods that change some attribute of a figure must notify the objects that depend on it. This is done by sending the willChange message to itself before changing the attribute, and sending the changed message to itself afterwards.
For example, EllipseFigure has an instance variable "ellipse'' that contains a rectangle representing its bounding box. It implements origin and extent by returning the origin and extent of ellipse. It implements translateBy: as
translateBy: aPoint
self willChange.
ellipse := ellipse translateBy: aPoint
self changed
In HotDraw, the dependents of a figure are almost always constraints (to create a constraint, see Constraints(5)).
Most figures have other attributes. For example, geometric figures like RectangleFigure and EllipseFigures have an interior color, a border color, and a border width. PERTEvents have duration, ending dates, and titles. Changing any of these attributes also requires notifying the dependents of the figure. An easy way to ensure that a figure gets the willChange and changed messages whenever it changes one of its attributes is to change the attribute in only one place, and to have that method send the messages to itself.
Each drawing element in a HotDraw application is a subclass of Figure, and must implement displayOn:, origin, extent, and translateBy:. In addition, a subclass of Figure can implement any method needed by the application.
To write displayOn:, see [ParcPlace 90] 1 . To let the user change the size or the color of a figure, see Changing drawing element attributes (3). To see how to implement complex figures like PERTEvents, see Complex Figures (4). To enforce constraints between different figures, see Constraints (5).
There are at least three ways to edit a figure's attribute; with a handle, with the figure's popup menu, or with a special tool. Each technique is appropriate in different cases.
The size of a figure and other numeric attributes are best edited with handles. Textual attributes like names, or numeric attributes that must be precise like dates, are best edited by displaying the text as part of the figure and letting the user edit it with the TextTool. Use of the TextTool implies that the figure is a CompositeFigure (see Complex Figures (4)).
A figure's handles method returns a collection of handles on the figure. The handles method for Figure returns resizing handles on the four corners, so it is common for a handles method to call it (with super handles) and add more handles. SelectionTrackHandle has class methods that create customized handles. For example, colorOf: will change the inside color of a figure, borderColorOf: will change the color of the border, and so on. For good examples of a handles method, see LineFigure or RectangleFigure. A figure's menu is returned by its menu method. By default, an operation in the menu is sent to the DrawingView. Operations whose selectors are in a collection returned by the menuBindings method of a figure are instead sent to the figure. The default menu defined in Figure implements cut and paste. See Figure for a simple example and TextFigure for a more complex example with hierarchical menus.
List the attributes of the drawing element that you want to edit. For each attribute, decide whether to edit it with a handle, an operation from the menu, or a tool, and update the handles method, the menu method, or the list of tools in the drawing editor.
To make new kinds of handles, see Handles (6). To make new kinds of tools, see Tools(8).
Some figures have a visual presentation with internal structure. For example, they may have attributes that are displayed by other figures. It should be possible to compose them from simpler figures.
Complicated figures like PERTEvent can be thought of as being composed of simpler figures. For example, a PERTEvent is a RectangleFigure with several TextFigures for the title, the duration, and the ending date. Complex figures like PERTEvent are subclasses of CompositeFigure. A CompositeFigure is a figure with other figures as components, and it displays itself by displaying its components. It has a bounding box that is independent of the bounding box of its components, and it will display its components only if they are inside of its bounding box. The selection tool and text tool will operation on its components when the left shift key is pressed. Custom tools can operate directly on the components, if you want.
In general, a figure should be a subclass of CompositeFigure whenever one of its attributes will be edited directly by a tool. The most common example is that an attribute is a string, and must be edited with the text tool. Instead of storing the text attribute in an instance variable, store it in a TextFigure. Do this by first ensuring that the attribute is read and written only by a pair of accessing methods. Instead of a stringvalued instance variable, make a TextFigurevalued instance variable, and make the string's accessing methods read and write it from the TextFigure.
This can be generalized for any kind of attribute that is represented by another figure. The attribute should be stored in the component figure, changes to the attribute result in changes to the figure, and changes to the figure result in changes to the attribute. If changes to one component might effect others then constraints should be used. (See Constraints (5)).
The initialize method of the complex figure must create the figure representing the attribute and add it to the complex figure. It may also need to create constraints. PERTEvent is a good example.
Complex figures should be a subclass of CompositeFigure, and figures that display one of its aspects should be a component of it.
To enforce constraints between the components of a complex figure, see Constraints (5).
Often an attribute of one figure is a function of the attributes of other figures. For example, in the PERT chart editor of Figure 1, the starting date of an event is the maximum of the ending dates of all the events that precede it, i.e. that have a connection to it. As another example, the endpoints of the lines connecting events depend on the locations of the events. Handles also depend on the figure to which they are attached.
Smalltalk has a standard way of keeping track of dependences between objects. Each object has a collection of dependents (usually empty), and the addDependent: and removeDependent: messages update this collection. Sending the changed: message to an object will cause the update: message to be sent to all its dependents. This same mechanism is used to maintain constraints in HotDraw.
HotDraw extends the standard Smalltalk class library by making constraints be objects. Traditionally each object that can depend on another defines its own update method. The problem with this is that many update methods are similar, so there is a lot of duplicated code. Making constraints be objects means one figure does not directly depend on another. Instead, the dependents of an object are always constraints, and updating a constraint will cause it to modify other figures in response.
Many of the HotDraw figures have the constraints that they need built automatically. For example, a line that connects two figures is just a LineFigure, except that there are two constraints that are created along with it. The first constraint depends on the starting figure and updates the starting point of the LineFigure, and the second depends on the ending figure and updates the ending point of the LineFigure. These constraints do nothing more than keep track of the two figures and the messages to be sent to get a position from one figure and to change the position of the other. These constraints are created by the LineFigure class startLocation:stopLocation: method, which creates a line connecting two figures.
Other times you will need to create constraints for a figure. There are two standard classes of constraints, PositionConstraint and MultiheadedConstraint. A PositionConstraint is typically used to maintain a constraint between the position of two figures. For example, it is used to connect a line to other figures. A MultiheadedConstraint depends on a set of figures.
Both kinds of constraints are customizable. A PositionConstraint refers to a position on a figure with a Locator, and also knows a message that it sends to the dependent object when the figure that it depends on changes. A Locator refers to a position on a figure by keeping track of the figure and knowing the message to send to the figure to learn its position. The "bounding box accessing'' protocol of class Figure defines methods to compute many positions, such as center, left, bottomRight, etc. A MultiheadedConstraint is created with a block that it evaluates whenever any one of the figures that it depends on changes.
It is not hard to create new kinds of constraints. A constraint must define the update: method, but otherwise its design is entirely up to you. Fortunately, simple uses of HotDraw will not require using constraints explicitly.
Each constraint in a drawing should be represented by a constraint object.
To add constraints between figures automatically when you add lines between them, see Adding Lines (10).
Handles vary in behavior. Sometimes handles change the size of a figure, sometimes they change its color, sometimes they create new lines. In general, one could imagine pressing on a handle performing any kind of operation. Moreover, handles can be attached to any part of a figure, and they move when the figure moves. This means that handles must be parameterized in some way.

Figure 4 Handles on Two Figures
Figure 4 shows some of the different kinds of handles in HotDraw. It has three figures in it, and the rectangle figure and the text figure have been selected and so have handles. The interior handles in the rectangle change its inside color and the color and the width of its border. Its corner handles change its shape. The rightmost handle on the text figure controls the width of the text field and the bottom handle controls the font size of the text. All the handles shown, except the one in the center of rectangle, are instances of SelectionTrackHandle. Like most of the Handle subclasses, SelectionTrackHandle is parameterized and so a new subclass rarely needs to be written.
SelectionTrackHandle changes an aspect of each selected figure, including its own. It is a subclass of TrackHandle, which only changes an aspect of its own figure. Both kinds of handle are parameterized by a message that the handle sends whenever it is dragged, with an argument that is the distance that the handle has been moved. Nearly any numeric attribute can be edited by a TrackHandle or a SelectionTrackHandle. The only precondition is that there must be a message to set the numeric attribute.
The expression SelectionTrackHandle widthOf: aFigure will create a handle that can change the width of a RectangleFigure or an EllipseFigure. It does this by sending the borderWidthBy: message to the figure, as can be seen in the definition of the widthOf: method, which specifies that the handle will be 10@0 off center of the figure, and will only "sense color'' (which means to use just the yvalue of the distance that the handle has been dragged as an argument to the borderWidthBy: message).
widthOf: aFigure
^self
on: aFigure
at: #offCenter:
with: 10@0
sense: #senseColor:
change: #borderWidthBy:
The handle in the center of the rectangle of Figure 4 is a ConnectionHandle, which is a handle that creates a line from its figure to another figure. A ConnectionHandle can be parameterized with a block taking the source and destination figures as arguments; that block is evaluated when the figure is created. See Adding lines (10).
If you do need to make a new subclass of Handle, you only need to define one method, invoke:. The argument of invoke: is a DrawingView, since it needs to access the controller (and hence the mouse), the drawing, and the display. Since invoke: gets sent whenever the user presses the mouse button on a handle, it is possible to make handles with any kind of behavior.
For example, Figure 5 shows the diagramming inspector, a HotDraw application that displays the interconnections between objects. Each box is an instance of ObjectFigure, and represents an object. When an ObjectFigure is selected, it presents handles for each instance variable that is nonnil. Clicking on one of these handles creates a new ObjectFigure representing the value of the instance variable, unless that object already has an ObjectFigure, in which case a line is drawn to it.
ObjectFigure requires a new kind of handle, one that is activated by clicking, not dragging. ReferenceHandle is a subclass of Handle that implements these new handles. It redefines the invoke: method to first find an ObjectFigure for the object, creating a new one if necessary. It then creates a line to the ObjectFigure and adds the line to the drawing. It takes several methods to implement all of this.

Figure 5 The Diagramming Inspector
A handle that changes only an aspect of its figure when it is dragged is an instance of TrackHandle. A handle that changes an aspect of all the selected figures when it is dragged is an instance of SelectionTrackHandle. A handle that creates a line from its figure to another figure is a ConnectionHandle. Handles that perform other functions will need to be new subclasses of Handle.
To create a ConnectionHandle, see Adding lines (10).
Drawings are often part of a complex user interface that includes text panes, buttons, lists, and so on. HotDraw is built on top of the Smalltalk80 Model/View/Controller framework, and so should be able to be a small part of a complex application.
In addition to Drawing and its components, a HotDraw application will have a DrawingView, a DrawingController, and a DrawingEditor. The DrawingView and DrawingController are designed to fit into the Model/View/Controller
framework and are rarely customized, so little needs to be said about them. The DrawingEditor is the model, and is responsible for keeping track of the drawing, the set of tools, the current tool, the menu of operations on the drawing (in distinction to the menus of operations on figures, for which each figure is responsible), and many of the operations on drawings. For example, the standard DrawingEditor class has methods for reading and writing drawings to files.
For simple applications of HotDraw, a subclass of DrawingEditor only needs to redefine the tools (See Tools(8)). For example, that is all that is needed for the PERTChart. The Diagramming Inspector of Figure 5 does not even have its own subclass of DrawingEditor, but just uses the default palette. However, complex user interfaces like that of the network editor of Figure 2 require more complex subclasses. The editor must not only support DrawingEditor protocol, it must also support the other panes of the user interface.
If an application only displays one drawing at a time then the model should be a subclass of DrawingEditor, but if there is more than one drawing then it is better to make the model be a completely new class. DrawingView is a pluggable view (see [ParcPlace]) so it can be parameterized with the messages to get the tools, menu, and drawing from the editor. To see how to parameterize a DrawingView, look in the instance creation protocol of the class methods of DrawingView.
The DrawingEditor is responsible for creating the user interface. This usually is nothing more than creating a toplevel window and embedding the palette and the DrawingView within it. However, a complex application like the network editor of Figure 2 requires a method for creating all the panes and connecting them together. A good example is the open method of NetworkEditor. For more on how to embed a DrawingView in a larger application, see Chapter 17 of [ParcPlace].
Make a drawing part of a complex application by making a DrawingView be a subview of the application's view and giving the DrawingView a model that responds to DrawingEditor protocol, i.e. that implements the currentTool, menu, drawing, and drawing: methods.
Tools represent the modes of the user interface to a drawing. Selecting a tool from the palette lets the user manipulate figures, create new figures, or perform operations upon a figure or the entire drawing. An important part of designing an editor using HotDraw is to design the set of tools that will be on the palette.
The tools method in DrawingEditor returns an ordered collection of tools that makes up the palette. These tools usually include the selection tool, creation tools for each drawing element that the user will create with a creation tool, a tool to scroll the drawing, and tools to move figures from front to back and from back to front. There are standard Tool subclasses for these tools, but it is also possible to define new subclasses of Tool.
Several of the subclasses of Tool are parameterized, so new subclasses of Tool are rare. CreationTool is parameterized by the class of the figure to create, the icon to display in the palette, and the cursor. Most subclasses of Figure define a class method creationTool that returns an initialized CreationTool that can be installed in the palette. EllipseFigure and RectangleFigure both contain good examples.
Two more parameterized classes of tools are DrawingActionTool, which performs an action on the drawing, and FigureActionTool, which performs an action on a figure. A DrawingActionTool is parameterized with two blocks, one that is evaluated when the tool is selected and the other when it is deselected. On the other hand, a FigureActionTool is parameterized with a single block that is evaluated when the mouse is clicked on a figure. DrawingActionTool has class methods loadTool and saveTool, which return tools to read and write the current drawing to a file, respectively. FigureActionTool has class methods that return tools to bring a figure to the front of the drawing, that send it to the back, and the delete it. To make other tools that perform single actions, use these methods as models.
The tools method of the DrawingEditor defines the tools that will be on the palette by returning an ordered collection of tools, which are instances of subclasses of Tool.
Constraints, handles and tools let a drawing react to a user, but cannot give a drawing a life of its own. Animation requires a controlling object to direct all the figures in a drawing.
Animation is provided in HotDraw by making a subclass of Drawing that defines the step method. This is the main reason that Drawing is subclassed. A drawing is repeatedly sent the step message whenever HotDraw is running. The purpose of the step method is to move each of the drawing's figures. For example, MovingDrawing simulates the nbody problem by giving each figure a velocity and assuming that figures exert forces on each other. Its step method is
step
animating ifFalse: [^super step].
"First, calculate the new velocities of each
figure by calculating the gravitational force
that each has on the others.''
self figures do:
[:fig1 | fig1 calculateForceFrom:
self figures] .
" Last, move each figure.''
self figures do: [:fig1 | fig1 step]
Typically there is some way to turn animation off, in this case by setting the ``animating'' variable of the moving drawing to false. The easiest way to set this variable is by a tool. The tool should be an instance of DrawingActionTool that is parameterized by a block that sends the startAnimation message to the drawing when the tool is selected and a block that sends stopAnimation when the tool is deselected. (See Tools (8)).
Animate a drawing by making a subclass for it that defines the step method to perform the next step in the animation.
Lines are often used to connect figures. Sometimes these connections have no semantics, i.e. they are a byproduct of other actions and deleting or moving them does not affect other figures. However, sometimes adding a line will result in constraints being added or other figures created.
The best way to create a connecting line from one figure to another is to add a ConnectionHandle at the point from which the line starts. By default, this line has no semantics. However, a ConnectionHandle can be parameterized with a block that is evaluated when the line is added. For example, adding a line from one PERTEvent to another means that the destination PERTEvent depends on the source. The handle to create these lines is created (in the handles method of PERTEvent) by
handle := ConnectionHandle
on: self
at: #rightCenter
class: LineFigure.
handle
action:
[:source :dest |
dest startConstraint
addSource: source endFigure].
The action block depends on the fact that PERTEvents have startConstraints (to compute the starting date) that are MultiheadedConstraints (see Constraints(5)), and so understand the addSource: message.
The user should create lines that connect figures by pressing a ConnectionHandle on one of the figures. ConnectionHandles can be parameterized so that connecting two figures with a line can perform some other action on them as well, such as adding a constraint or testing whether a connection is allowed.
HotDraw is available by anonymous ftp from