3.2 Object structures

The next thing we may wish to do is illustrate how objects relate to each other graphically. There are four principal ways in which objects interact. The most primitive of these is association, which indicates that a type is specified in terms of another type.

Associations

UML shows associations using the notation given in Figure 5. The rôlenames can be regarded as labels for the cardinality constraints or as attributes of the type furthest away from where they are written. For example, holder can be thought of as an attribute of Account, constrained to hold between 1 and 2 values of type Customer.

Figure 5 Associations in UML.

When an association has interesting properties in its own right it should be represented as a new type with the appropriate attributes and two new associations to the types it originally connected. For example, the association married-to between the types Man and Woman would be a plain vanilla association in an HR system but needs to be a type for a wedding registration system, with attributes including the people involved, the date of the marriage and so on. UML has a special notation for such 'association classes' but it is entirely redundant and so we will not use it (see Appendix C if you are interested).

Association types should not be introduced merely to remove many-to-many relationships as one would do during relational database design. They should only be used where they can be given meaningful names. A counter-example would be the relationship between products and regulations.

Converting associations into types can also be used to distinguish between rôles and players: the types that play the rôles. Figure 6 shows a set of transformations or refinements of the same model to illustrate the point. Notice that the notion of having a job can be reified into a type Job to allow the capture of work-related information. Converting this type back into an association allows us to be explicit about the rôles involved. This is rarely a good idea during specification but it is useful to know that it is possible, because at design time it may indicate how to design a class representing a plug-point.

Graham et al. (1997a) showed that bi-directional associations of the kind depicted in Figures 5 and 6 violate encapsulation and thereby compromise reuse. This kind of diagram is adequate for sketching out preliminary models and discovering the vocabulary of the domain but when documenting reusable components it is preferable to think of associations as one-directional pointers corresponding to the rôlenames in the figure. The reason for this will become even clearer when we discuss invariants.


Figure 6 Distinguishing rôles and players.

We already have the notion of the interface to an object containing an attribute and its facets. We can view associations as (generalized) attributes. In this sense an attribute contains zero or more values of a nominated type; an association stores values of a nominated user defined (or library) type. The only difference is that attributes point at 'primitive' types. What is primitive is up to the analyst and a key criterion for making this decision is whether the type (class) should appear on the class model diagrams.

Typical associations in an HR application might include ones that show that employees must work for exactly one department while departments may employ zero or more employees. These could be shown as follows.

worksIn: Dept(1,1) is an attribute of Employee.
employs: (Employee,0,n) is an attribute of Dept.

We regard attributes as split into two sorts: pure attributes and (attributes representing) associations. Technically, there is no difference between the two but the associations of pure attributes are not shown in association structure diagrams, mainly to avoid clutter. The default cardinality for a pure attribute is (0,1); if the attribute is non-null this should be shown as a facet.

This definition of associations is slightly different from that of methods and notations such as OMT, Shlaer-Mellor, Coad or UML. These view associations as external to objects and their metamodels require a new primitive to accommodate them. This shows up most clearly when bi-directional associations are used.

As we have seen, one of the two basic and characteristic features of object-orientation is the principle of encapsulation. This says that objects hide the implementation of their data structures and processing, and are used only via their interface. An object-oriented model uses object types to represent all concepts and divides these types (or their implementations as classes) into a public interface representing the type's responsibilities and an implementation that is hidden completely from all other parts of the system. The advantages of this are that it localizes maintenance of the classes and facilitates their reuse via calls to their interfaces alone. Modifications to classes are avoided by creating specialized subclasses that inherit basic responsibilities and can override (redefine) them and add new ones.

Bi-directional associations violate encapsulation. Stating that class A is associated with class B in some way or other is a statement of knowledge that concerns both classes. There are three obvious approaches to storing this knowledge:

Thus, separating objects from relationships violates encapsulation and compromises reuse. However, we will demonstrate later how the knowledge can indeed be split between the two types without loss of integrity, using invariants encapsulated in the objects.

Another way to violate encapsulation is to write remarks about associations, including constraints, on the type diagrams rather than encapsulating them with the interfaces. Constraints on the way objects are related can be written on UML diagrams near the associations that they refer to and connected to them by unadorned dotted lines. Clearly no class encapsulates them. For a particularly striking example of how foolish and unnecessary this is, consider the {or} constraint shown in Figure 7(a). This example was actually taken from the original UML documentation (www.rational.com). It shows that a bank account can be held by a person or an organization, but not by both.

 

Figure 7 (a) A UML constraint violating encapsulation; (b) how appropriate use of polymorphism makes the constraint unnecessary.

The amazing thing is that any object-oriented designer should know perfectly well that, instead of this bad design, one should utilize inheritance polymorphism to represent the disjunction, as in Figure 7(b). We could also specialize Account to the same end.Here exactly the same information is conveyed and encapsulation is preserved. In addition we have forced the discovery of a very reusable class and - we would assert - an analysis pattern. Fowler (2000) calls half of this pattern extract superclass.

One special kind of association, and one that is characteristic of object-oriented models, is the generalization association, which is a class attribute whose value is the list of classes (or types) that the class inherits features from. Diagrams of these associations are called inheritance structures. Figure 8 illustrates some of these. Note that inheritance can be single or multiple. Also note that type inheritance can be distinguished from class inheritance because in type inheritance only the specification and no implementation is inherited. UML lacks specific notation for this but uses a dotted line with an arrowhead identical to those in Figure 8 to indicate interface inheritance (which it calls realization).

Associations can be inherited (or derived) and this is shown by placing a / mark on the association line at the rôlename end and/or before the attribute name. A good CASE tool would allow automatic suppression or display of derived associations.

 

Figure 8 Single and multiple inheritance structures in UML.

Unfortunately, as shown by Wills (1991), it is not possible to make any general statement about the way cardinality constraints are inherited except that it depends on the entire interface of the subtype. This, of course, depends on how we interpret the diagrams: as a domain model or as part of a requirements description. Thus, we must make the weakest assumption: that inherited cardinalities are zero-to-many. Additional knowledge of the subtype and the domain allows us to strengthen this. For example, if all full-time employees work in exactly one department, we may wish to allow part-timers to work in several but, of course, they must work in at least one. We will be able to say more about this topic when we come to deal with invariants.

Aggregation and Composition

Another special kind of association is aggregation or composition. This occurs when there is a whole-part relationship between instances of a type. However, great care should be taken when using it unless you really understand what you are doing. It is somewhat dangerous for type modelling but will be essential when we examine use cases later.

An aggregation or composition indicates that a whole is made of (physically composed of) its parts. A good heuristic test for whether a relationship is a composition relationship is to ask: 'if the part moves, can one deduce that the whole moves with it in normal circumstances?'. For example, the relationship 'is the managing director of' between People and Companies is not a composition because if the MD goes on holiday to the Alps, the company does not. On the other hand, if his legs go the Alps then the MD probably goes too (unless he has seriously upset some unscrupulous business rivals).

Figure 9 Aggregation and composition in UML.

Strictly in UML, aggregation and composition are represented by empty and filled diamonds respectively as shown in Figure 9 and represent programming language level concepts. In UML the empty diamond of aggregation designates that the whole maintains a reference to its part, so that the whole may not have created the part. This is equivalent to a C++ reference or pointer construction. The filled diamond signifies that the whole is responsible for creating its 'parts', which is equivalent to saying in C++ that when the whole class is allocated or declared the constructors of the part classes are called followed by the constructor for the whole (Texel and Williams, 1997). It is clear to us that this has little to do with the analysis of business objects; nor does the definition of composition in terms of ownership and lifetime constraints on instances (Rumbaugh et al., 1999). We continue to use the terms composition and aggregation interchangeably for the common-sense notion of assembled components. The semantics of this notion were explored in detail by Odell (1994) whose argument may be summarized as follows.

Odell classifies composition relationships according to three decisions: whether they represent a structural relationship (configurational), whether the parts are of the same type as the whole (homeomeric) and whether the parts can be detached from the whole (invariant). This evidently factors APO into eight types. He then discusses six of them and names his different interpretations of composition as follows:

  1. Component-integral (configurational, non-homeomeric and non-invariant).
  2. Material (configurational, non-homeomeric and invariant).
  3. Portion (configurational, homeomeric and non-invariant).
  4. Place-area (configurational, homeomeric and invariant).
  5. Member bunch (non-configurational, non-homeomeric and non-invariant).
  6. Member-partnership (non-configurational, non-homeomeric and invariant).

We will only use configurational, invariant composition. All other types of so-called composition (such as the manages relationship alluded to above) are handled by either associations or attributes (which are merely a special case of associations). What difference these distinctions make to the programmer is arguable. We like the idea of aggregation as a form of refinement: the constituents are what you find if you look closer.

Figure 10 Composition structure for a yacht.

As with inheritance, composition is directional: it is improper for the part to know what whole it belongs to because this would compromise reuse. To emphasize this we have adorned the example of a composite yacht in Figure 10 with arrowheads. Each composition link can have a cardinality constraint at the part end as also shown. If a similar constraint were to be used at the whole end, not only would this compromise encapsulation, but we would have to introduce a distinction between type level and instance level. We now think that a better way to handle this is to use Odell's notion of a 'power type' (Martin and Odell, 1998). A power type is just a type whose instances are subtypes of another type. Types may have more than one power type corresponding to separate subtype discriminators. For example, if we divide employees into male and female subtypes we could create (slightly redundantly in this case) a power type called GenderType; but if we classify them according to job type a different (and perhaps more useful) power type may be used. Returning to aggregation, if we regard a bicycle as being composed of a frame and some (two?) wheels then a particular wheel can belong to only one bike. However, the type of wheel that it is can be fitted to several types of bike. This leads to the need to distinguish type level from instance level aggregation unless we introduce a power type corresponding to the catalogue description of the wheel: its WheelType. This phenomenon is referred to as 'reflection'. The term is usually used when there is some facility to extend the model 'at run time'; the idea is that the analyst defines a model in which there is a type of types, which can be used at run time to add new types, but over which relationships are nevertheless asserted in the base specification language.

Figure 11 Derived composition dependencies.

Like other kinds of association, composition can be inherited and the same convention as for ordinary associations is used to show this: a / mark on the link. A subtype inherits the components of all its supertypes. Consider a type A with subtypes B and C. A also has components A1 and A2. Class B has components B1 and B2. The composition structure for Class B therefore includes two derived components as shown in Figure 11. The derived dependencies are shown in grey for greater clarity. We have not shown the derived dependencies for C. It is easy now to see how including all such derived links can quickly make diagrams unreadable, which is another reason why CASE tools should let them be toggled on and off.

Some authorities regard the notion of composition as undefinable and others definable only in terms of some specified set of operations defined on the whole. We recommend that composition is used sparingly in business object modelling it is absolutely essential when modelling actions as objects.

Depend-encies

UML also allows dependencies of arbitrary type between types and classes. These are shown by the labelled, dashed arrows. The label is a stereotype. The most usual and useful dependencies relate to interfaces and packages and we shall return to them later in those contexts.

Usage

Usage relationships signify that not only does a client know about the existence of a feature of a server but that it will actually send a message to exercise that feature at some point. Ordinary associations might never be exercised. The difference between an association and a usage dependency is akin to that between knowing the address of the editor of the Financial Times and being its Wall Street correspondent. This is not, as many of our critics have claimed, an implementation concept but a key part of the semantics of a model. Saying that two classes are associated does not imply that the structural link between them will ever be traversed. For example, there may be many relationships in a database that are there to support ad hoc queries that may never be made by any user. A usage link on the other hand states that there will be some interaction or collaboration. The existence of usage links removes the need for a separate notion for collaboration graphs as found in RDD (Wirfs-Brock et al., 1990). One class 'uses' another if it is a client to the other class acting as a server. Any associations introduced may subsequently be replaced by more specific usage or (more rarely) composition relationships. This kind of relationship is also extremely important in requirements engineering and business process modelling. It is a concept missing from both OML and UML, although in UML one can use a dependency labelled «uses» to represent the idea. Henderson-Sellers (1998) argues for the inclusion of a uses relationship in OML.

Figure 12 Usage associations.

Figure 12 shows an easily memorable icon to represent the stereotype «uses», and the more usual notation.