«The Amulet Prototype-Instance Framework Brad Myers Carnegie Mellon University Richard G. McDaniel Carnegie Mellon University Robert C. Miller ...»
Carnegie Mellon University
Research Showcase @ CMU
Institute for Software Research School of Computer Science
The Amulet Prototype-Instance Framework
Carnegie Mellon University
Richard G. McDaniel
Carnegie Mellon University
Robert C. Miller
Carnegie Mellon University
Follow this and additional works at: http://repository.cmu.edu/isr
This Working Paper is brought to you for free and open access by the School of Computer Science at Research Showcase @ CMU. It has been accepted for inclusion in Institute for Software Research by an authorized administrator of Research Showcase @ CMU. For more information, please contact email@example.com.
The Amulet Prototype-Instance Framework Brad A. Myers, Richard G. McDaniel, and Robert C. Miller December 22, 1998
To appear in:
Object-Oriented Application Frameworks, vol. 3, edited by Mohamed Fayad and Douglas C. Schmidt.
New York: John Wiley & Sons, 1999.
Human Computer Interaction Institute School of Computer Science Carnegie Mellon University Pittsburgh, PA 15213-3891 (412) 268-5150 FAX: (412) 268-1266 firstname.lastname@example.org http://www.cs.cmu.edu/~amulet Abstract Amulet is a new kind of object-oriented framework for user interface development that is based on a prototype-instance object system instead of the conventional class-instance object system. In a prototype-instance object system, there is no concept of a “class” since every object can serve as a prototype for other objects, and any instance can override any methods or data values. Amulet is also differentiated by high-level encapsulations of interactive behaviors, and by the ubiquitous use of constraints, which are relationships that the programmer declares once and then are enforced by the system. The result is that programs written using the Amulet framework have a different style than those written with conventional frameworks. For instance, Amulet applications are typically constructed by combining instances of the built-in objects, rather than by subclassing the built-in classes or writing methods. Amulet is written in C++ and is portable across Windows NT and 95, Unix X/11, and the Macintosh.
Copyright © 1998 -- Carnegie Mellon University
This is a revision of:
Brad A. Myers, Richard G. McDaniel, Robert C. Miller, Alan Ferrency, Andrew Faulring, Bruce D. Kyle, Andrew Mickish, Alex Klimovitski, and Patrick Doane “The Amulet Environment: New Models for Effective User Interface Software Development,” IEEE Transactions on Software Engineering. vol. 23, no. 6. June, 1997. pp. 347-365.
This research was sponsored by NCCOSC under Contract No. N66001-94-C-6037, Arpa Order No. B326.
The views and conclusions contained in this document are those of the authors and should not be interpreted as representing the official policies, either expressed or implied, of NCCOSC or the U.S. Government.
Myers Object-Oriented Application Frameworks The Amulet Framework -1 Creating user interface software has proven to be very difficult and expensive, because it is often large and complex, and challenging to implement, debug, and modify. Most of today’s application frameworks for user interfaces still leave far too much of the application to the programmer. The Amulet user interface development framework tries to overcome this problem by supplying high-level support for the insides of application windows, which most other frameworks ignore. For example, whereas most frameworks do a good job of managing the creation of windows and the main menus, they leave the contents of the window to be programmed at the low-level window manager level, accepting events like “mouse left-button down at 30,50” and using routines like “draw-line.” In contrast, Amulet supplies high-level support for the graphics and interactive behaviors of application-specific objects. The result is that many behaviors, such as creating, moving, selecting, and manipulating objects, cut/copy/paste, save and load, undoing of operations, etc., can often be incorporated into applications without writing any methods at all.
A key reason that Amulet provides a high level of support is that all of the user interface objects are available at run-time for inspection and manipulation through a standard protocol. This allows high-level, built-in utilities to be provided which, in other toolkits and frameworks, must be re-implemented for each application. For example, the graphical selection handles widget can get the list of objects in a window, find out which ones are graphical, and move and resize a selected object, all using standard protocols, even if the objects are custom-created and application-specific.
The result is that creating applications in Amulet is quite different than in other frameworks. In fact, much of Amulet programming is done without writing methods (Myers 1992), but instead by creating instances of built-in objects, setting their properties, and combining them into groups.
In addition to incorporating innovations into its own design, Amulet has an open architecture to enable user interface researchers and developers to easily investigate their own innovations. For example, Amulet is the first system that supports multiple constraint solvers operating at the same time, so that researchers might be able to investigate new kinds of constraint solvers. The undo model also supports new designs. The “widgets” (the UNIX name for elements of the toolkit, like scrollbars, buttons and menus; sometimes called “controls” on the PC) are implemented in an open fashion using the Amulet intrinsics so that researchers can replace or modify the widgets. The goal is that researchers will only have to implement the parts that they are interested in, relying on the Amulet library for everything else. In addition, we aim for Amulet to be useful for students and
general developers. Therefore, we have tried to make Amulet easy to learn, and to have sufficient robustness, performance and documentation to attract a wide audience.
Amulet, which stands for Automatic Manufacture of Usable and Learnable Editors and Toolkits (Myers 1997), is being developed as a research project at Carnegie Mellon University. It is implemented in C++ and runs on X/11, Windows 95, Windows NT, and the Macintosh. Applications created using Amulet can simply be recompiled to run on any of the machines.
1. Layered Design The Amulet framework is divided into a number of layers (see Figure 1). These layers include an
interface to the window managers; and novel models for objects, constraints, input, output, and commands; and a set of widgets. The following sections describe the overall design of each of these.
1.1. Gem: Abstract Interface to the Window Managers Amulet provides a portable interface to various window managers called “Gem,” which stands for the Graphics and Events Manager. Gem uses C++ mechanisms to provide a simple graphics and input interface used by the rest of Amulet. Any code written using Gem will port to different windowing systems (Windows 95 or NT, Macintosh, or X/11) without change. Most other toolkits and frameworks only provide an interface at the Gem level, or require that all graphics use the underlying low-level window manager drawing Myers Object-Oriented Application Frameworks The Amulet Framework -3 interface. However, typical Amulet users never see the Gem interface, since the higherlevel parts of the Amulet framework provide access to the same capabilities in an easierto-use way. We export the Gem interface mainly for advanced Amulet users. If the programmer wants to make something very efficient, calling Gem directly may be appropriate.
Figure 2: When an instance is made of the node prototype, an instance is also made of each of the parts.
1.2. Object System The “Ore” (Object Registering and Encoding) layer of Amulet implements a prototypeinstance object and constraint system (Lieberman 1986) on top of C++. In Amulet’s prototype-instance object system, there is no concept of a “class” since every object can serve as a prototype for other objects. An object is comprised of a set of slots. A “slot” has a name and can hold a value of any type, for example the slot named Am_LEFT might hold the value 10. Slots are similar to member variables or instance variables in other object systems. A new object is created by making an instance of another object, which is called the prototype. Creating an instance is like making a copy of an object, except for the way that inheritance works. One of the innovations in Amulet is that if an object has parts, then the instance will also get instances of the parts (see Figure 2). We call this “structural inheritance.” An instance starts off inheriting all of its slot values from the prototype, so that initially the instance and its prototype have all the same values. Then, the programmer will typically set some slots of the instance with new values. If a slot is not set in the instance, then its value will change if the prototype’s value is later changed. The object system is
dynamic in that slots in objects can be added and removed from objects at run time, and the types in slots can also change.
For example, the following Amulet code creates a Node_Bitmap as an instance of the built-in Am_Bitmap object, and then sets the Am_IMAGE slot to the appropriate picture.
Am_Object Node_Bitmap = Am_Bitmap.Create().Set(Am_IMAGE, simple_rect_pic);
An important feature of Amulet’s object system is that there is no distinction between methods and data: any instance can override an inherited method as easily as inherited data. In a conventional class-instance model such as Smalltalk or C++, instances can have different data, but only sub-classes can have different methods. Thus, in cases where each instance needs a unique method, conventional systems must use a mechanism other than the regular method invocation, or create a new subclass and a single instance of that subclass each time. For example, a button widget might use a regular C++ method for drawing, but would have to use a different mechanism for the call-back procedure used when the user clicks on the button, since each instance of the button needs a different call-back.
In Amulet, the draw method and the callback use the same mechanism.
As an example, the following sets the “DO” method of the command in a button to be my_method (command objects are described below). Then, my_method will be called whenever the user hits the button.
Amulet’s object system also contains many other features that may be useful for programmers. An automatic memory management mechanism, which uses a reference counting scheme, manages Amulet’s objects. A complete set of querying functions makes it easy to determine objects’ properties at run-time. These are used by the debugging facilities described below, and they can also be useful for application programs. For example, a program can query the list of slots of an object, and whether each slot is local or inherited. For each slot, the name of the slot (e.g. “LEFT”), current value (e.g., 10), and type of the current value (e.g., Am_INT_TYPE) can be retrieved. Since there is no distinction
between method and data slots, this one mechanism is used to discover all of the properties of an object.
Programming with a prototype-instance object system is a quite different style than is used in conventional object-oriented languages. Much of the code is devoted to listing the slots and default values for prototype objects, usually at initialization time, and then creating instances, possibly overriding some slots, as the program is running. This “declarative style” of programming seems to be intuitive and less error prone, and it makes it easier for more of the code to be created and analyzed by interactive design tools, like interface builders.
There are many other advantages of the prototype-instance model. Having no distinction between classes and instances, or between methods and data, means that there are fewer concepts for the programmer to learn and a consistent mechanism can be used everywhere. Another advantage of the prototype-instance object system is that it is very dynamic and flexible. All of the properties of objects can be set and queried at run time, and interactive tools can easily read and set these properties. In fact, most of today’s frameworks and toolkits implement some form of “attribute-value pairs” to hold the properties of the widgets, but Amulet’s object system provides significantly more flexibility and capabilities.
Instead, the operand of the new operator must be a fixed class, leading to large case
statements and other inflexible and error-prone constructs. In contrast, since one can create an instance of any object in a prototype-instance object system, one can store a reference to an object in a variable and later use the variable to create a new object:
Am_Object obj_to_create = Am_Rectangle; //Typical Amulet code … Am_Object new_object = obj_to_create.Create();
Amulet’s predecessor, Garnet, also used a prototype-instance object system (Myers 1992). Amulet’s design is more complete and flexible, and we fixed a number of problems we experienced with Garnet. For example, we added the ability for a programmer to declare that certain slots are not inherited, so that each instance will start out with no value for that slot. For example, the Am_Drawonable slot of a Window object holds the maMyers Object-Oriented Application Frameworks The Amulet Framework -6 chine-specific pointer to the underlying window-manager window, and should not be inherited by instances of the window. Each instance of a Window must create and assign its own value for the Am_Drawonable slot. Finally, it is worth pointing out that Amulet is able to provide dynamic slot typing, a dynamic prototype-instance system, and constraints in C++ without using a preprocessor or a scripting language.