Some Patterns for Relationships James Noble
MRI, School of MPCE, Macquarie University, Sydney.
[email protected]
September 2, 1996 Abstract
Relationships between objects are almost as important to designs as objects themselves. Most programming languages do not support relationships well, so programmers must implement relationships in terms of more primitive constructs. This collection of patterns describe how objects can be used to model relationships within programs. By using these patterns, programs and designs can be smaller, more exible, and easier to understand and maintain.
Introduction Relationships, also known as collaborations or associations, are very important in the object oriented design model. For example, collaborations make up one third of the Class/Responsibility/Collaboration design technique [22], and Rumbaugh has claimed relationships are complementary to (and as important as) objects themselves [16]. Unfortunately, most object oriented programming languages do not support relationships well. For example, design relationships may be unidirectional or bidirectional; they may associate one object with one other object, one object with many other objects, or many objects with many others; they may be complete | an object may always be related to other objects | or partial, sometimes relating objects and sometimes not. Programming languages support relationships mainly through attributes (also known as variables, slots, or data members). Attributes generally always relate one object to one other object, and provide unidirectional, one to one, complete, relationships. This means that programmers must somehow build up other relationships using the raw materials the languages provide | objects, messages, and attributes [18]. This paper presents a collection of patterns for implementing relationships. Although these patterns do not form a complete pattern language, there is some structure to these patterns, which is illustrated in Figure 1. The diagram illustrates two relationships between patterns in the language. One pattern can re ne another pattern, providing more detail on how to implement that pattern. Alternatively, two patterns can con ict, meaning that the two patterns are mutually exclusive, each providing a dierent solution to a common problem [25]. 1
Relationship Attribute
Relationship Object
Attribute Collection Mutual Null Friends Object Object Object Refines:
Master And Slaves
Connected Group
Timed Relationship
Sender Argument
Conflicts:
Figure 1: Structure of the Patterns
Pattern
Relationship Attribute Relationship Object Attribute Object Collection Object Mutual Friends Master and Slaves Sender Argument Null Object Timed Relationship Connected Group
Problem
How can you model a very simple relationship? How can you model a big, important, or common relationship? How do you model an important one-to-one relationship? How do you model a one-tomany relationship? How can you represent an important bidirectional relationship? How can you manage a relationship? How can an object identify itself to other participating objects? How can you represent a partial relationship? How can you represent a temporal relationship? How can you coordinate several relationships?
Solution
Make an attribute to represent the relationship. Make a Relationship Object to represent the relationship. Make an Attribute Object to model this relationship. Make a Collection Object to model this relationship. Represent a mutual relationship as a series of separate unidirectional relationships, carefully keeping all these relationships synchronised. Choose one object as a master object, and have it manage its slaves. An object should identify itself via a Sender Argument. Make a Null Object which models the missing object. Make a Timed Relationship which captures temporal information. Make the relationships into a Connected Group.
Figure 2: Summary of the Patterns Figure 2 summarises the problems dealt with by this collection of patterns, and the solutions they provide. The content of these patterns should not surprise any object oriented software practitioner. Rather, these patterns attempt to capture well known design folklore, describing techniques, and illustrating when these techniques are appropriate. The patterns are written in a modi ed Portland form. Each begins with a question (in italics) describing a problem, followed by a description of the pattern's context and forces. A boldface \Therefore:" introduces the solution (also italicised) followed by the consequences of using the pattern, an example of its use, and some known uses 2
and related patterns. Three patterns in this collection has been described elsewhere | they are italicised in Figure 1. Rather than repeat full descriptions here, these patterns are summarised at the end of the paper.
Fundamental Relationship Patterns The rst two patterns in this collection describe two fundamental ways to model relationships | either using attributes, or using objects.
1 Relationship Attribute How can you model a very simple relationship? Simple, directed, one-to-one relationships are very common in object oriented models. Generally, such relationships carry very little weight in the application domain | the objects themselves are important, but the concept of the relationship is coincidental. In these relationships, the fact that the participating objects are related is important to only one of the related objects. Changes in the relationship | for example, one object being replaced by another | are only important insofar as they record the changing circumstances of that object in the program domain. The objects participating in the relationship initiate such changes directly, and may be uninterested in the precise state of the relationship. Because these kinds of relationships arise so often, they need to be simple to represent in a program, so that they are easy to write, and can be immediately understood by programmers later reading the program. For similar reasons, they must be implemented so that they impose a minimal overhead on the program | both in terms of the space required to represent the relationship, and the time taken to manipulate it. Therefore: Make an attribute to represent the relationship. Object oriented programming languages provide attributes (variables, slots, data members) which are local to each object instance. These attributes are very simple to use to implement one-to-one relationships, and are implemented eciently in every object oriented programming language.
Example
Consider an object which models an insurance quotation. Each Quotation object must record the name and address of the client to which it belongs, the sum insured, and any particular risk factors. The Quotation doesn't care about the values of any of these objects, and they can be changed as necessary (presumably via the Quotation object's interface) without any consequent eects on the program's model of the domain. Because the relationships are very simple, all this information can be stored as attributes of the Quotation object.
3
Comments
This pattern is fundamental to object oriented design, just as object's attributes (that is, state) are fundamental to object oriented programming. Although this pattern is simple, it is not necessarily trivial, and (in my experience) needs to be learned, especially by programmers with a predilection for modelling attributes using arrays or dictionaries. More importantly, it provides alternatives to the other patterns in this language which deal with more complex relationships. This pattern couples the object with the attribute to the object referred to by the attribute | this should not be a problem, since the coupling is present in the domain. Relationship Object (2) is a complementary pattern which describes how to represent important relationships with objects. Rumbaugh [19] presents one view of the dierent purposes of relationships and attributes.
2 Relationship Object How can you model a big, important, or common relationship? Objects are related to other objects. These relationships can be very complex, involving many participating objects which are essentially peers. Relationships can embody concepts and constraints from the programs' domain, which can require complex state or behaviour to implement. Some relationships, such as several objects being members of a sequence or being arranged as a tree, occur commonly throughout programs. Relationships are often implemented directly, using features of programming languages such as pointers or containment [17, 2]. This has several disadvantages. The relationship is dispersed among its participating objects | it cannot be easily identi ed within the program, and thus cannot be easily replaced or changed. Participating objects are tightly coupled, so a change to the relationship necessitates a change to one or more participating objects. Participating objects' cohesion is reduced, because they must model a portion of the relationship, as well as the abstractions they represent. Therefore: Make a Relationship Object to represent the relationship. Remove any methods or variables associated with the relationship from the participating objects, and move them into the Relationship Object. Change the implementations of the participating objects so that they refer to each other via the Relationship Object. The Relationship Object may need to use internal subordinate objects to implement the relationship, but these should not be visible through the Relationship Object's interface.
Example
Consider modelling an airline timetabling system. The core of such as system is a relationship between Flights and Cities. This is many-to-many (a ight may serve many cities, a city may be served by many ights), partial (not all ights serve every city, and vice versa) and bidirectional. 4
Distributing the implementation of this relationship over the Flight and City objects would unnecessarily complicate both these objects, especially if there are a variety of scheduling constraints (such as pricing structures, availability, and guaranteed connections) which involve a variety of dierent Flights. Introducing a Relationship Object | such as a Timetable | simpli es this design (see Figure 3). All the details of the relationship are collected into a single object, the Timetable. This object can use whatever internal implementation structures are needed for an ecient implementation, and these can be changed as necessary without aecting Flight or City. The Timetable can implement any additional constraints or behaviour required by the relationship.
Figure 3: Relationship Object .
Comments Introducing Relationship Objects increases the number of objects in the program, which can increase memory footprint. Accessing objects indirectly via Relationship Objects can reduce execution eciency. Relationship Objects which distribute
control among their participating objects can very easily obscure the ow of control throughout the whole program. Relationship Objects are currently under discussion in the literature on object oriented design methodology [20, 12, 7, 17]. They are not universally accepted, for example, the relationships in the Composite and Chain Of Responsibility patterns from Design Patterns are implemented directly using pointers, rather than explicit Relationship Objects [5]. ParcPlace Smalltalk provides several examples of Relationship Objects. ObjectWorks introduced DependentsCollection objects to record and manage the relationship between an object and its dependents [14], where the earlier Smalltalk-80 implemented this relationship using a global dictionary [6]. Relationship Attribute (1) is a complementary pattern which describes how to represent simple one-to-one relationships eciently. Observer [5] and Mediator [5] describe how Relationship Objects can coordinate the behaviour of their participating objects.
5
Patterns about Common Relationship The following patterns describe how some common relationships can be modelled. The three patterns cover one-to-one, many-to-one, and bidirectional relationships.
3 Attribute Object How do you model an important one-to-one relationship? Some one-to-one relationships are important in themselves, rather than simply connecting two objects. This importance is often due to the underlying domain, that is, a change in the relationship represents a change in an important parameter of the domain, which the program must deal with. Alternatively, otherwise simple one-to-one relationships may also be important to a program due to their position in the program's architecture. That is, other objects in the program (which may not participate directly in the relationship) may need to be noti ed if the relationship changes. Again, the importance of the relationship can be judged by the consequences of a change to the relationship. Therefore: Make an Attribute Object to model this relationship. An Attribute Object is essentially an object which represents a single variable. It should have a single attribute to hold the variables value, and should understand two messages | an accessor to retrieve the value of the variable, and a settor to change the value. To use an Attribute Object, make it an attribute of the source object of the relationship, and assess the relationship by sending messages to the Attribute Object, rather than to the source object.
Example
Constraint solvers maintain constraints between set of variables. To integrate with other programs, they need to be able to access and change these variables. By representing constrained variables explicitly (CVar objects) as Attribute Objects, they can be used directly by the constraint solving engine (Solver object) | see Figure 4.
Figure 4: Attribute Object
6
Comments
Like other relationship object patterns, this pattern reduces the coupling and increases the cohesion of the objects involved. Using Attribute Objects can complicate the program structure by introducing yet more small objects, and can increase the time and space costs of the program. Attribute Objects can reduce the clarity of program text, since simple assignment statements must be replaced by messages sent to Attribute Objects. VisualWorks's ValueModel framework is based on this pattern [23]. In particular, the ValueHolder object is a generic Attribute Object. The Cooldraw [4], Unidraw [21], and QOCA [8] constraint solvers all use Attribute Objects to represent variables explicitly: the last two uses are mentioned in Design Patterns [5]. Observer [5] can be used to link Attribute Objects to the objects which depend on the relationship. Attribute Objects often form a Connected Group (10).
4 Collection Object How do you model a one-to-many relationship? Directed, one-to-many relationships are almost as common as one-to-one relationships. Because they are common, such relationships need to be implemented as easily and as eciently as possible. Unfortunately, in most object-oriented programming languages, one-to-many relationships are rather more dicult to implement than one-to-one relationships. The Relationship Attribute (1) pattern does not extend cleanly to relationships where one object is linked to more than one object. Although an object can have multiple attributes, each attribute must be accessed individually | the attributes cannot be managed as a group. Multiple attributes can be used to implement multiple one-to-one relationships, but not one-to-many relationships. Therefore: Make a Collection Object to model this relationship. Most object oriented language libraries provide a variety of container or collection objects which can be used to represent one-to-many relationships. The \one" object simply stores a collection which holds the \many" objects participating in the relationship.
Example
Consider modelling an insurance policy which can have endorsements attached. This can be implemented by the Policy object having a pointer to the rst Endorsement, and every Endorsement having a pointer to the next Endorsement (see Figure 5). This has a good physical analogue: the rst paper endorsement is stapled to the policy document, and subsequent endorsements are stapled on top. Unfortunately, this has the disadvantage that all the information about the relationship is dispersed among the Policy and Endorsement objects | no single object implements the relationship. If the relationship needs to be changed, both the Policy and Endorsement objects will be aected. This program can be simpli ed by using a List Collection Object to model the relationship. The Policy class simply keeps a List of all its Endorsements (see 7
Figure 5: Many Relationship Attributes Figure 6). If necessary, the List can be replaced with a specialised collection object (perhaps an EndorsementList) to implement any additional constraints or behaviour required in the relationship.
Figure 6: Collection Object
Comments Using a Collection Object instead of multiple embedded pointers usually simpli es a program. Using Collection Object does introduce an extra runtime object, and
the indirection involved can reduce execution eciency. Collections are the most common kind of Relationship Object (2), and they form the core of many class libraries [5, 6, 2]. Most collections implement one-tomany unidirectional relationships, although some, such as Smalltalk's Dictionaries, can implement many-to-many relationships.
5 Mutual Friends How can you represent an important bidirectional relationship? You have a bidirectional relationship (also known as an undirected or mutual
8
relationship) where all participating objects are equally important. A bidirectional relationship may be a one-to-one, a one-to-many, or even a many-to-many relationship. In a bidirectional, relationship, a change in any one participating object may aect all other objects in the relationship, and each participating object needs to be easily accessible from every other object. Unfortunately, object oriented programming languages support for bidirectional relationships is even less than their support for unidirectional relationships. While unidirectional one-to-one relationships can be modelled with pointers (Relationship Attribute (1)), and unidirectional one-to-many relationships modelled with collections (Collection Object (4)), there are no obvious language or library constructs which can model bidirectional relationships. Therefore: Represent a mutual relationship as a series of separate unidirectional relationships, carefully keeping all these relationships synchronised. The simplest kind of Mutual Friends involves a one-to-one bidirectional relationship, where each participating object needs to be able to access the other object eciently, at any time. This can be modelled as two one-to-one unidirectional relationships, with one of the objects chosen as a slave and the other a master | see Master and Slaves (6). The relationship should be established by the slave sending a message to the master, probably passing itself as a Sender Argument (7). The master should then establish its unidirectional relationship, then send a private message back to the slave, so that the slave can establish its end of the relationship. Bidirectional one-to-many relationships can be modelled very similarly, except that the master needs to use a one-to-many unidirectional relationship, such as a Collection Object (4). Bidirectional many-to-many relationships can be implemented by using many collections, but obviously require rather more care.
Example
Consider the relationship between Owners and Cars. This is one-to-many (one person may own several cars, but each car has a single legal owner) and bidirectional (a person may need to inspect the cars they own, and the police may need to nd a car's owner). This is implemented by making cars and owners Mutual Friends. An Owner object understands messages to acquire and dispose of Cars, and uses a List to record the Cars owned. A Car object has an owner attribute which can only be set by its Owner, but which can be queried by other objects (see Figure 7).
Figure 7: Mutual Friends 9
.
Comments
This pattern increases the coupling between objects, and decreases their cohesion, because each participating object depends upon the details of the other object's implementation. This pattern is dicult to write correctly, because both ends of the relationship must be kept synchronised. The Smalltalk class hierarchy includes a bidirectional relationship between subclasses and superclasses. A class and its subclasses are mutual friends, with the superclass as the master. Each class keeps a collection of it subclasses, and each subclass has a pointer to its class [6]. Master and Slaves (6) can make both using and implementing bidirectional relationships much easier. By choosing one participating object as the master of the relationship, other objects can access the relationship as if it were unidirectional. If the control ow across a relationship is primarily in one direction, you may be able to model the relationships as if it were a unidirectional relationship, and use a Sender Argument (7) for access in the other direction.
Patterns about Organising Relationships The following two patterns describe how relationships should be organised | that is, how the protocols used to manage relationships should be designed.
6 Master and Slaves How can you manage a relationship? Object orientation focuses on individual, isolated objects, and models computation as the sending of messages to particular, distinguished objects. Unfortunately, every relationship has the potential to involve at least two objects | a relationship which only ever involves one object isn't much of a relationship! So, especially when initially establishing the relationship, how do know which object to send your messages to? That is to say, which object should provide the interface for managing the relationship? One alternative is that every participating object should provide a management interface. If the participating objects are very much alike, they can use inheritance to share the required behaviour, and so the relationship can be managed by sending a message to any participating object. For example, assume a person object can marry another person object. Since the marrying behaviour is presumably stored in the de nition of all person objects, it should not matter which object receives the marry message. In more general cases, however, relationships are not symmetrical, and there will be great dierences between the objects participating in a relationship. To manage such a relationship by sending messages to any participating object, every object would need to provide a interface to manage the relationship, and cooperate with every other participating object to implement the required behaviour.
10
Therefore: Choose one object as a master object, and have it manage its slaves.
The master object should provide an interface to manage the whole relationship. Participating objects (the slaves) should send messages to the master to establish, query, and dissolve the relationship. The slaves' implementation of the relationship can be made much simpler, because they should only be invoked by the master. Language mechanisms (such as C++'s friends or Eiel's selective export) can be used to enforce this restriction. In a one-to-many relationship, the \one" object is usually the better choice for the master, since this centralises the responsibility for maintaining the relationship in a single object. If one object in the relationship usually creates the other participating objects, this object should be the master. If the relationship is directional, the source of the relationship is the obvious candidate for the master object.
Example
Consider a Person who owns several Dogs. How can this relationship be established? There are two alternatives: either a message can be sent asking a person to adopt a dog, as in: johnSmith.adopt( do); or, a dog may decide to follow a person, as in: do.follow(johnSmith); The problem domain semantics indicate that the rst solution is preferable: johnSmith should adopt do. The relationship is one-to-many (one Person may own many Dogs), and the relationship is directed from a Person to their Dogs.
Comments
The problem this pattern addresses relates more to the solution domain | object orientation | than to the problem domain. In particular, it re ects the asymmetry of object orientation's singly dispatched messages. In a language with multiple dispatch, relationships could be established by sending a message to all the involved objects at once. However, even in multiple dispatch languages, it can be simpler to manage a relationship via a single master object, rather than via all the objects participating in the relationship. The Smalltalk libraries show several instances of this pattern [9]. For example, a superview acts as a master of its subviews, that is, subviews are managed by sending messages to their superview. Similarly, Smalltalk objects are masters of their dependents, that is, dependents are managed by sending messages to the objects upon which they depend. The master's protocol can use Sender Arguments (7) so participating objects can identify themselves.
7 Sender Argument How can an object identify itself to other participating objects? You have a relationship where one participating object needs to identify itself to
11
other partners. Perhaps this is a bidirectional relationship which is only explicitly stored in one direction, and you want to traverse the relationship in the opposite direction. Perhaps this is a one-to-many relationship, where a \many" object needs to identify itself to the \one". Perhaps an object needs to join a relationship, and the other participating objects are not yet cognisant of that object. One way to model this is to mark the objects participating in the relationship in some way. For example, in a one-to-many relationship, every \many" object could be given a unique name or number. When a \many" object needs to communicate with the \one" object, it could use its name to identify itself. This has the important drawback that the name must be stored in the \many" object | which means the \many" objects must be changed to store the name tag. The name cannot be stored in the \one" object because this doesn't solve the problem | the many object must still somehow identify itself before its name can be found. Therefore: An object should identify itself via a Sender Argument. Protocols for relationships should include an extra argument to identify the participating object which the message involves. When a participating object sends such a message, it should send self (this in C++, Current in Eiel) as the argument of the message. The message's receiver (typically the master object of the relationship) can use the Sender Argument to identify the appropriate participating object.
Example
When johnSmith adopts do, as in: johnSmith.adopt( do); do is passed as a Sender Argument.
Comments The dependency mechanism in VisualWorks sends update messages to dependent objects whenever a subject object signals it has changed. The change noti cations include the subject that has changed as a Sender Argument, so that an object which is a dependent of multiple subjects can determine which subject has changed [15]. The Tarraingm program visualisation systems uses a composite hierarchy of watcher objects to monitor the programs that are to be visualised [11]. A subwatcher may need to pass error information up to its superwatcher. The subwatcher passes itself as a Sender Argument, so that the superwatcher can identify the subwatcher to which the error information belongs. This pattern depends on the direction of control ow between the object participating in the relationship, since a Sender Argument can only be passed along in the direction of the control ow. If the control ow is not in the right direction for this pattern, you may need to make the participating objects Mutual Friends (5). A Sender Argument is often used in the Observer and Mediator patterns [5].
12
Patterns for Complex Relationships This section concludes the paper with brief paraphrases of three recently written patterns concerning more complex relationships, to illustrate how these (and other) patterns could be incorporated into a language based on the patterns in this paper.
8 Null Object How can you represent a partial relationship? A partial relationship is a relationship which only involves some of a particular sort of object. For example, a program may include a \one to zero-or-one" relationship, such as a large number of person objects, only some of which own dog objects. One way to implement partial relationships uses conditional code, for example code which tests whether a Relationship Attribute (1) named dog is nil. Unfortunately, this approach clutters programs with a large amount of conditional code. Therefore: Make a Null Object which models the missing object. A Null Object replaces a real object when the relationship is partial. It has the same interface as the real object, but behaves as if there was no object in the relationship. Client code does not need to know whether or not there is another real object at the end of the relationship. For example, a nullDog would have the same interface as a real dog object, but would actually do nothing. This pattern has been described by Bruce Anderson [1] and Bobby Woolf [24].
9 Timed Relationship How can you represent a temporal relationship? A temporal relationship changes over time. For example, a relationship may change from describing future plans to describing current state, and then to describing history. This relationship is often modelled as a variety of separate relationships, often split across dierent systems. However, all three of these separate relationships have similar behaviour and data. Therefore: Make a Timed Relationship which captures temporal information. A Timed Relationship is a Relationship Object (2) which includes temporal information. It should include information about the state of the relationship, as well as the dates and times when the state has changed. A Timed Relationship should not only store current information but also historical and planning information. For example, a room booking system should not only store the current room bookings, but also former bookings (for history) and future bookings (for planning). This pattern has been described by Lorrie Boyd [3].
10 Connected Group How can you coordinate several relationships? Some relationships are themselves interrelated. For example, a CASE tool dia-
13
gram may include several graphic objects related by constraints, so that lines are connected to icons, boxes must equal the width of the text they contain, and classes related by inheritance must be vertically aligned. Each of these relationships can be implemented as a Relationship Object (2). However, changing one of these relationships (for example, by moving an icon) may cause many of the other relationships and objects to be updated. Updating every object and relationship at every change may cause many unnecessary updates, and may produce incorrect results since the updates will not be synchronised. How can you coordinate these updates? Therefore: Make the relationships into a Connected Group. A Connected Group is a set of Relationship Objects (2) which are interrelated, typically by a Connection Manager object. Whenever a participating object or relationship changes, the Connection Manager is invoked. The Connection Manager acts as a Mediator [5], so all the participating objects and relationships can be updated as necessary, in the correct order. The Connection Manager is eectively a Relationship Object (2) which represents a relationship between other relationships. This pattern has been described by Jiraong Li [10].
Acknowledgements Many thanks are due to John Grundy for his collaboration on Explicit Relationships in Object Oriented Design [12] which inspired this pattern language. Some of this work was performed at the Centre for Object Technology Applications and Research of the University of Technology, Sydney | the support of issc Australia, a joint venture of ibm and Lend Lease, is gratefully acknowledged. The dog owner example is due to Meilir Page-Jones [13], and thanks are due to John Potter and David Holmes for their comments on this draft.
References [1] Bruce Anderson. Null object, 1996. Reviewed at EuroPLOP. [2] Grady Booch. Object Oriented Analysis and Design with Applications. Benjamin Cummings, second edition, 1994. [3] Lorrie Boyd. Patterns of association objects in large scale business systems, 1996. Reviewed at EuroPLOP. [4] Bjorn N. Freeman-Benson. Converting an existing user interface to use constraints. In Proceedings of the ACM Symposium on User Interface Software and Technology, 1993. [5] Erich Gamma, Richard Helm, Ralph E. Johnson, and John Vlissides. Design Patterns. Addison-Wesley, October 1994. [6] Adele Goldberg and David Robson. Smalltalk-80: The Language and its Implementation. Addison-Wesley, 1983. 14
[7] Ian Graham, Julia Bischof, and Brian Henderson-Sellers. Associations considered a bad thing. Journal of Object-Oriented Programming, 1996. To appear. [8] Richard Helm, Tien Huynh, Kim Marriott, and John Vlissides. An objectoriented architecture for constraint-based graphical editing. In Third Eurographics Workshop on Object-Oriented Graphics, 1992. [9] Wilf Lalonde and John Pugh. Inside Smalltalk, volume 2. Prentice-Hall, 1991. [10] Jairong Li. Connected group, 1996. Reviewed at EuroPLOP. [11] James Noble, Lindsay Groves, and Robert Biddle. Object oriented program visualisation in Tarraingm. Australian Computer Journal, 27(4), November 1995. [12] James Noble and John Grundy. Explict relationships in object-oriented development. In TOOLS 18. Prentice-Hall, 1995. [13] Meilir Page-Jones. What Every Programmer Should Know About ObjectOriented Design. Dorset House, 1995. [14] ParcPlace Systems. ObjectWorks Smalltalk User's Guide, 4.1 edition, 1992. [15] ParcPlace Systems. VisualWorks Smalltalk User's Guide, 2.0 edition, 1994. [16] James Rumbaugh. Relations as semantic constructs in an object-oriented language. In OOPSLA Proceedings, 1987. [17] James Rumbaugh, Michael Blaha, William Premerlani, Frederick Eddy, and William Lorensen. Object-Oriented Modeling and Design. Prentice Hall, New Jersey, 1991. [18] James E. Rumbaugh. Models for design: Generating code for associations. Journal of Object-Oriented Programming, 8(9), February 1996. [19] James E. Rumbaugh. A search for values: Attributes vs. associations. Journal of Object-Oriented Programming, 9(3), June 1996. [20] C. Tanzer. Remarks on the object-oriented modelling of associations. Journal of Object-Oriented Programming, 7(9), 1995. [21] J. M. Vlissides and M. A. Linton. Unidraw: A framework for building domainspeci c graphical editors. ACM Transactions on Information Systems, 8(3), 1990. [22] Nancy M. Wilkerson. Using CRC Cards: An Informal Approach to ObjectOriented Design. SIGS Books, 1995. [23] Bobby Woolf. Understanding and using the ValueModel framework in VisualWorks Smalltalk. In Pattern Languages of Program Design. Addison-Wesley, 1994. [24] Bobby Woolf. Null object, 1996. Reviewed at PLOP. [25] Walter Zimmer. Relationships between design patterns. In Pattern Languages of Program Design. Addison-Wesley, 1994. 15