* A conditional indicator. This is a question mark (?), optionally followed by a Boolean expression in parentheses. The iteration and conditional indicators are mutually exclusive.

3. A *return value name* followed by an *assignment sign* (`“:=“`). If present this indicates that the procedure returns a value designated by the given name. The use of the same name elsewhere in the diagram designates the exact same value. If no return value is specified, then the procedure operates by side effects.

4. *The name of the message*. This is an event name or operation name. It is unnecessary to specify the class of an operation since this is implicit in the target object.

5. *The argument list of the message*. The arguments are expressions defined in terms of input values of the nesting procedure, local return values of other procedures and attribute values of the object sending the message.

Argument values and return values for messages may optionally be shown graphically using small data flow tokens near a message. Each token is a small circle, with an arrow showing the direction of the data flow, labeled with the name of the argument or result.

### 18.2.3 State machine diagrams

Scenarios are used to help understand how the objects within the system collaborate, where as state diagrams illustrate how these objects behave internally. That is, state diagrams relate events to state transitions and states. The transitions change the state of the system and are triggered by events. The notation used to document state diagrams is based on that developed by Harel [Harel et al 1987, Harel 1988] and termed *Statecharts*.

Statecharts are a variant of the finite state machine formalism which reduces the apparent complexity of a graphical representation of a finite state machine. This is accomplished through the addition of a simple graphical representation of certain common patterns of finite state machine usage. As a result, what might be a complex subgraph in a “basic” finite state machine is replaced by a single graphical construct.

Each state diagram (statecharts are referred to as state diagrams in UML) has a start point and a point which the state is entered and may have an exit or termination point at which the state is terminated. The state may also contain concurrency as well as synchronization of concurrent activities.

Figure 18.4 illustrates a typical state diagram. This state diagram describes a (very) simplified remote control locking system. The chart indicates that the system first checks the identification code of the hand held transmitter. If it is the same as that held in the memory it will allow the car to be locked or unlocked. For the *car locking* situation, the windows are also closed and the car is alarmed.

<figure><img src="image_89.png" /><figcaption>Figure 18.4: An example State diagram</figcaption></figure>

A state diagram consists of a start point, events, a set of transitions, a set of variables, a set of states and a set of exist points. These are described briefly below:
### 18.2.3.1 A start point

A start point is the point at which the state diagram is initialized. In the figure, there are actually four start points indicated ('Start', 'lock', 'close' and 'unlock'). The 'Start' start point is the initial entry point for the whole diagram, while the other three indicated start points are for sub State diagrams.

Any preconditions required by the State diagram can be specified on the transition from the start point (for example, in the figure the transmittedID must be the same as the memoryID memorized by the system). It is the initial transition from which all other transitions emanate. This transition is automatically taken when the State diagram is executed. Note that the initial 'Start' point is not equivalent to a state.

### 18.2.3.2 Events

Events are one way asynchronous transmissions of information from one object to another. They can possess parameters with names and types. The general format of an event is: *eventName*(*parameter:type, ...*). Of course many events do not have any associated parameters.

### 18.2.3.3 A set of transitions

These are the statements which move the system from one state to another. In the state diagrams each transition is formed of four (optional) parts:

1. an event (e.g. lock).

2. a condition (e.g. transmittedID = memoryID)

3. the initiated event (e.g. ^EngineManagementUnit.locked)

4. an operation (e.g. /setDoorToLock)

The event is what triggers the transition, however the transition will only occur if the condition is met. If the event occurs and the conditions are met, then the associated operation is performed. An operation is a segment of code (equivalent to a statement or program or method within a programming language) which causes the system state to be altered. Some transitions can also trigger an event which should be sent to a specified object. For example, the above example sends an event *locked* to the *EngineManagementUnit*. The process of sending a global event is a special case of sending an event to a specified object.

The syntax of an event is therefore:

event(*arguments*) [*condition*] ^*target.sendEvent(*arguments*) / *operation(*arguments*)*

### 18.2.3.4 A set of state variables

These are variables referred to in a State diagram (for example memoryID is a state variable). They have the format *name: type = value*.

### 18.2.3.5 A set of states

A state represents a period of time during which an object is waiting for an event to occur. It is an abstraction of the attribute values and links of an object. A state is drawn as a rounded box containing the (optional) name of the state. A state may often be composed of other states (the combination of which represents the higher level state). A state has duration, that is it occupies an interval of time.

A state box can contain two additional sections, one section containing a list of state variables and the other section containing a list of triggered operations (as illustrated by Figure 18.5).

<figure><img src="image_90.png" /><figcaption>Figure 18.5: State box with state variables and triggered operations</figcaption></figure>
Operations can be of the following types:

* entry (executed when the state is entered). These are the same as specifying an operation on a transition. They are useful if all transitions into a state perform the same operation (rather than need to specify the same operation on each transition). Such operations are considered to be instantaneous.

* exit (executed when the state is exited). These are less common than entry actions and indicate an operation performed before any transition from the state occurs.

* do (executed while the state is active). These are operations which start on entry to the state and terminate when the state is exited.

* event. A specified event can also trigger off an operation while within a particular state. For example, the event help could trigger the help operation which in the state active.

Each operation is separated from its type by a forward slash ("/"). The ordering of operations is: `operation on incoming transitions -> entry operations -> do operations -> exit operations -> operations on outgoing transitions.`

State diagrams allow a state to be a single state variable, or a set of substates. This allows for complex hierarchical models to be developed gradually as a series of nested behavior patterns. This means that a state can actually be a State diagram in its own right. For example, *car alarmed* is a single state, however the *car locked* state, is actually another State diagram. Notice that the transition from *car alarmed* to *accepted* jumps from an inner state to an outer state.

The dotted line down the middle of *car locked* state indicates that the two halves of that state run concurrently. That is, the car is locked as the windows are closed.

A special type of state, called a history state, is used to represent a state which must be remembered and used the next time the (outer) state is entered. The symbol for a history state is an H in a circle.

### 18.2.3.6 A set of exit points

Exit points are used to specify the result of the State diagram. They also terminate the execution of the State diagram.

## 18.3 Deployment diagrams

<figure><img src="image_91.png" /><figcaption>Figure 18.6: Nodes in a Deployment Diagram</figcaption></figure>

The elements in Figure 18.6 are called nodes and represent processors (such as PC's and servers) and devices (such as Printers and Faxes). A node is thus a resource in the real world upon which we can distribute and execute elements of the (logical) design model. A node is drawn as a three -dimensional rectangular solid with no shadows. The <<device>> stereotype designation of the Fax and Printer indicates that these nodes are not processors. That is, they do not have any processing ability (at least not from the point of view of the model being constructed). It is also possible to show how many nodes are likely to be involved in the system. Thus the Order Entry PC is of order * ( 0 or more ), while there will be exactly one server, printer, fax etc. Finally, we have also indicated the roles of the associations
between nodes and their stereotype. For example the *Receiving* association on one PC which will employ a type of ISDN connection (which has yet to be specified).

## 18.4 Summary

This chapter (and the last) have presented an overview of the Unified Modeling Language (or UML). The UML is an attempt to develop a third generation object oriented modeling language for use with a variety of object oriented design methods. It can be used for documenting the design of client -server, real-time, distributed as well as batch style applications. It captures the best elements of the notations used by a number of existing design methods including Booch, OMT and Objectory while attempting to remain extensible, simple, clear and (relatively) concise. Many CASE tool vendors are already committed to supporting the UML and it is being presented to the OMG by a consortium of organizations as the basis of a standard notation for object-oriented systems development.
# 19. The Object Modeling Technique

## 19.1 Introduction

This chapter provides an overview of the Object Modeling Technique (OMT). It should not be treated as a definitive, nor complete, description of the method. For that the reader should see [Rumbaugh et al, 1990] and [Derr 1995]. Instead, this chapter provides an introduction to the basic phases and a summary of the steps which form each phase. As such it should provide an insight into an object oriented design (OOD) method.

The chapter first introduces the OMT methodology. The analysis phase is then described in some detail. The use of use case analysis is included. This is not actually part of OMT (in fact it is part of the Objectory method of [Jacobson et al 1992]) but is a useful adjunct to the method. The dynamic and functional models of the analysis phase are described in the next chapter. The design phase (which is then introduced) and the implementation phase are described in detail in the next chapter.

## 19.2 The OMT methodology

The OMT methodology consists of several phases. These phases progress a design from (relatively) early requirements analysis through detailed design to the implementation. The phases are thus: the analysis phase, the design phase and the implementation phase.

The *analysis phase* is concerned with understanding and modeling the application and the domain within which it operates. The OMT book suggests that the initial input to the analysis phase is a problem statement which describes the problem to be solved and provides a conceptual overview of the proposed system. This problem statement may be a textual description (as suggested by the authors) or it might be a more formal description as provided by a technique such as OOA or one of the structured analysis methods in the software engineering field.

The *design phase* is made up of two sub-phases, the system design phase and the object design phase. The system design sub-phase is concerned with the overall architecture of the system. This architecture is based on the information provided during the analysis phase (in particular the object model).

The object design sub-phase attempts to produce a practical design by refining, optimizing and reviewing the models produced during the analysis phase. This therefore involves moving the focus away from conceptual objects and towards computer implementation objects. It also involves identifying appropriate algorithms, ensuring efficient communication between objects and accounting for the flow of control and concurrency issues.

The final phase, the *implementation phase*, considers how the design should be implemented. It considers, amongst other issues, mapping a design onto an object oriented language, a database system as well as non object oriented languages.

## 19.3 Analysis phase

The analysis phase is concerned with producing a precise, concise, understandable, and correct model of the real-world. This is done by constructing a series of models. In particular OMT defines an *object model*, a *dynamic model* and a *functional model*. OMT does not attempt to construct use case diagrams to help understand how the system is used and what the main functions of that system are. However, it is probably a very good idea to start the analysis phase by carrying out a use case analysis. We will therefore consider use case analysis before continuing with the OMT method.
OMT is a prescriptive method, it provides guidance on how the various models it generates should be constructed. In the remainder of this section we will consider the guidelines given.

### 19.3.1 Use case analysis

The intention of the use case analysis is to identify how the system is to be used and what the system is expected to do in response to this use. This involves identifying the external users of the system (human or machine) and the required system functionality. The users of the system and the roles they play are referred to as *actors* while the functions requested and their sequence are called *use cases*. The combination of the actors and the use cases are referred to as the *use case model*. Each of these will be considered in more detail below.

#### 19.3.1.1 Actors

An actor can be anything which interacts with the system. This means they can be human users, other computer systems, dumb terminals, senso rs, devices to be controlled etc. However, they not only represent the user, but also the role that the user plays at that point in time. For example, in a small company the accountant might act as the data entry clerk at one time, the internal auditor at another and as the payroll administrator at yet another time. Each of these roles would be represented by a different actor, even though they would all be performed by the same person. To stress the difference between actors and users [Jacobson et al 1992] says that they think of an actor as “a class, that is, a description of a behavior” while a user is described as playing “several roles” which are “many actors”.

Identification of the actors in the system is not trivial and as [Jacobson et al 1992] point out “all actors are seldom found at once”. Jacobson goes on to state that a “good starting point is often to check why the system is to be designed?”. Having done this it should be possible to identify the main users of the system and what they will need to do with that system. From these users and their needs, actors can be identified. Identification of such actors is usually relatively straight forward, but it is often much more difficult to identify non-human actors. In general, as the rest of the use case model develops, these actors “come out in the wash”.

The notation used (in this book at least) for actors is based on that presented by the UML in the last chapter.

For a simple bank account system the actors may be the *customer*, the *bank clerk* and the *bank manager*.

#### 19.3.1.2 Use cases

When an actor interacts with the system it is for a specified purpose. The achievement of this purpose involves following one or more steps. If there are multiple steps there may be a specific sequence to these steps in order to achieve the desired purpose. For example, if you are attempting to obtain money from a “hole in the wall machine” (cash dispenser/ATM) then you must first input your card, type in your PIN, select the type of transaction you require, specify the amount of the transaction, take the card and then take the money. If you attempt to change this sequence you will fail to obtain your money (e.g. you type in your PIN before inputting your card!). The combination of the purpose, and the specified sequence of steps, forms a use case.

A use case can be represented in a number of ways, the two most common are as natural language and as state transition diagrams. Whichever approach is adopted, the same information should be captured. The most appropriate form may depend on the availability of support tools for the state machine notation.

The collection of all use cases for a system defines the functionality of that system. The identification of use cases is based on the identification of actors. Each actor will need to do one or more things to the system, each of these uses will be a use case. That is, each actor must have at least one use case (and may be involved in many use cases). Whether each use case is unique (or merely a duplication of another use case) may only be determined once all the use cases are identified and defined. To help in identifying the use cases, the following questions can be asked [Jacobson et al 1992]:

* What are the main tasks of each actor?
* Will the actor have to read/write/change any of the system information?

* Will the actor have to inform the system about outside changes?

* Does the actor wish to be informed about unexpected changes?

Early in the analysis process it is often enough just to identify the possible uses cases and not to worry about their details. Once a reasonable set of uses are identified, it may be possible to analyze the systems requirements in greater detail in order to flesh out the uses cases. Use case identification tends to be iterative and should not be treated as a single step process for all but the simplest of systems.

Having identified the uses cases, we can identify the steps performed within each use case. In many cases this can help to identify omissions and over generalizations in any problem statement or domain understanding, that we may have.

For the simple bank account system mentioned above, a typical use case might be:

```
Check account is started by Customer when they want to find out their current balance.
This is accomplished by:
1. typing in their account number followed by the PIN
2. requesting the current balance of their account (this may be on screen or on a print out)
3. receiving the balance
4. acknowledging receipt of the balance
5. logging off the system
```

Notice that the first element of the use case is a statement of its purpose. This is then followed by the sequence of steps performed by the use case. The steps described above are referred to as the *basic course* of the use cases. That is, the normal way in which the use case will *execute*. In general uses cases only possess a single basic course. However, they may possess one or more *alternative courses*. These alternatives deal with exceptional situations or errors. For example, what if the customer does not have a current account, what happens if they do not accept receipt of their balance?

### 19.3.1.3 Use case models

The identification of the actors and the uses cases together help to specify the limits of the system. That is, anything that is an actor is outside the system, whereas anything that is a use case is within the system boundaries. This means that it is possible to draw a line around the uses cases indicating the boundary of the system. This is very useful in helping to identify the bounds of the software project as a whole in terms which both a developer and a user *can* understand. This can help to clarify misunderstandings between users and developers over what is the system's responsibility and what is not. A partial use case model for the simple account system is presented in Figure 19.1.

<figure><img src="image_92.png" /><figcaption>Figure 19.1: Use case model</figcaption></figure>

### 19.3.1.4 Interface descriptions

Having defined the actors in the system and the uses they make of the system, the next step is often to specify the interfaces between the actors and the system. For human users of the system, these interfaces may well be graphical user interfaces (GUI's) and may be drawn using a drawing tool or mocked up using some form of interface simulation software. These interfaces can be very useful in
confirming the users' needs, their anticipated use, as well as helping to keep them involved in the
development. As the use cases specify the sequences of operations to be performed, the GUI's can be
made to mimic the desired system behavior. This is a good way of confirming that the use case is
correct. For non-human interfaces any proposed communications protocols can be defined and checked
(for example, that the interacting system is capable of sending and receiving the appropriate
information).

19.3.1.5 Relating the use case model to OMT

The use case model dictates the formation of the models in OMT's analysis phase as indicated in Figure 19.2. For example,

* it helps identify the primary objects in the object model,

* it helps specify the top level behavior of the system in the dynamic model,

* it helps determine the inputs and outputs provided by, and expected by, the actors for the functional model.

In addition use cases may also have an influence on how the system will be organized into subsystems as they indicate associated behaviors. In the implementation phase the use cases may be used to identify suitable scenarios and expected results for integration or system testing.

<figure><img src="image_93.png" /><figcaption>Figure 19.2: How the use case model can be used in OMT</figcaption></figure>

19.3.2 Object modeling

The first step in requirements analysis phase, of the OMT method, is the construction of the object model. The object model is essentially comprised of the class and object diagrams described in the last chapter. As with BOOCH and the UML it is the object model which is the core of the OMT representation. As we have already encountered object models before we will not attempt to explain what they are other than to state that the object model “shows the static data structure of the real -world system and organizes it into workable pieces”.

The information for the object model comes from:

* the problem statement (written in natural language according to OMT),

* requirements analysis process such as OOA,

* the domain experts,

* general knowledge of the real world,

* the use case model (if you have constructed one).

An interesting point to note is that OMT claims that object models promote communication between computer professionals and application-domain experts (what do you think?).

OMT suggests the following steps as being an appropriate way in which to construct an object model.

1. Identify objects and classes.

2. Prepare a data dictionary.

3. Identify associations (including aggregations) between objects.

4. Identify attributes of objects and links.

5. Organize and simplify object classes using inheritance.
6. Verify that access paths exist for likely queries.

7. Iterate and refine the model.

8. Group classes into modules.

However, you should not take the sequence of these steps too strictly: remember analysis and design are rarely completed in a truly linear manner. It is likely that some steps will be performed to greater depths as the whole process proceeds. In addition, some steps may (will) lead to revisions in other steps and once an initial design is produced it will doubtless require revisions. Therefore you should consider that they are a sequence of processes which should be performed. The order of which may be influenced by the domain, the expertise available, the application etc. However it is probably a good idea always to start with the process of identifying objects and classes.

We will consider each of the above steps in more detail below.

### 19.3.2.1 Identify objects and classes

The first step in constructing the object model is to identify the objects in the domain and their classes. Such objects may include:

* physical entities such as petrol pumps, engines and locks

* logical entities such as employee records, purchases and speed

* soft entities such as token, expression or data stream

* conceptual entities such as needs, requirements or constraints.

As long as they make sense for the application and the domain then they are a candidate object or class. The only things you should avoid are objects which might relate to the proposed computer implementation.

OMT suggests that you start identifying these candidate objects by looking in the textual problem description. It indicates that classes are often found by identifying nouns in the description and making them into classes. However, if you have a more formal problem specification it might be easier to identify a set of potential classes. If you have used the use case analysis method you may also be able to identify the top most objects directly from the use case diagrams.

Do not worry about getting it right at this point or about identifying classes which should not be there. Inappropriate classes will be filtered out later on - for the moment, attempt to find anything which could be a class.

Once you have a comprehensive list of candidate classes you can discard any unnecessary and incorrect ones. This is done by using the following criteria:

*Is any of the classes redundant?* That is, two or more classes express the same information. For example, customer and user might just be different names for the same thing.

*Is any of the classes irrelevant?* That is, are they outside the scope of the system to be built even though they are part of the domain. For example, although porters work in a hospital they are probably not relevant to a hospital bed allocation system.

*Is any of the classes vague?* That is, do they represent ill defined concepts? For example, history provision is vague - what is it a history of?

*Is any of the classes really attributes of other classes?* For example, name, address, salary, job title tend to be attributes of an object employee rather than objects in their own right.

This can be a tricky one as it is often possible to represent something as both a class or an attribute. One way of handling this is to leave the decision until later. If the class possesses only one type of information and has no operations then it should probably have been an attribute.

*Is any of the classes really operations?* If a class appears to describe an operation that is applied to objects and not manipulated in its own right then it is not a class. For example, a telephone call is a sequence of actions performed by a caller. In the implementation you may wish to make this an object, however at this point in the design we are trying to produce a model of the application domain (i.e. not an implementation model).

*Does the name of the class represent its intrinsic nature and not its role in the application?* For example, the class Person might represent an object in a restaurant booking system, but the class Customer is a better representation of its role in the system.

*Is a class really an implementation construct?* For example, things such as process, algorithm, interrupt, exception are implementation concepts and tend not to be related to the application domain.
### 19.3.2.2 Prepare a data dictionary

A data dictionary provides a definition for each of the terms / words being used in the evolving analysis models. Each entry precisely describes each object class, its scope, any assumptions or restrictions on its use and its attributes and operations (once they are known).

### 19.3.2.3 Identify associations between objects

The next step is to identify any (and all) associations between two or more classes. This is done by looking for references between two classes. OMT suggests that a good place to look for these relationships is to examine the problem description for verbs or verb phrases between known objects. In particular it identifies the following types of relationships:

* physical location (next to, part of, contained in)

* directed actions (drives)

* communication (talks to)

* ownership (has, part of)

* satisfaction of some condition (works for, married to, manages)

Again OMT exhorts you to identify all possible relationships and not to worry at this point about getting it right.

If you have used a method such as OOA you might already have some knowledge about the relationships between the classes in the domain, if not then considering which classes are likely to need to work with which other classes (i.e. the accounts clerk may need to work with the salaries clerk) is also a good place to start. This process will be simplified if you have performed a use case analysis.

Once you have a set of candidate associations, OMT provides a detailed set of criteria to help in refining them. The criteria are:

*Is the association one which is between eliminated classes?* If one of the classes involved in the association has been eliminated then the association should be eliminated.

*Are any of the associations irrelevant or implementation associations?* Eliminate any associations that are outside the scope of the application domain (including implementation related associations).

*Are any associations transient?* An association should be a structural property of the applications domain. For example, *interacts with user* is a temporary association in a hotel booking system.

*Are any of the associations ternary?* Although the OMT notation and indeed the UML notation allow ternary associations, they are not encouraged. Instead you are encouraged to decompose these associations into binary ones (they are easier to implement, maintain and understand!).

*Are any of the associations derivable?* OMT suggests that you should remove any associations which can be derived from other associations. However you should be wary of removing such associations as they may be critical to understanding the domain relationships. That is, if an association can be replaced by two existing associations, only do so if the semantic meaning of the two associations can be combined to provide the same semantic meaning as the one to be removed. A good example of this is that a *GrandparentsOf* relationship can be replaced by two *ParentOf* relationships.

Having removed inappropriate associations, you can now consider the semantics of the associations you have left. To do this you should use these criteria:

*Are any of the associations misnamed?* That is, do they actually reflect what they represent? Are they named after their use or the relationship they indicate?

*Add role names where appropriate.* As described in the last chapter, role names describe the role that a class plays in the associations from the point of view of the other class.

*Are there any qualified associations?* That is, are there any associations which require a qualifier to identify a unique object.

*Specify multiplicity on the associations.* That is, indicate how many objects are involved in the association. By default all associations are 1 to 1 associations. Where no multiplicity is specified check that these really are 1 to 1 links.

*Are there any missing associations?* Check that all reasonable associations are present. This may need to be done in consultation with the domain expert.
#### 19.3.2.4 Identify attributes of objects

OMT suggests that attributes correspond to nouns followed by possessive phrases, such as "the color of the car" or "the position of the cursor" in the problem statement. Although the authors do admit that attributes are less likely to be fully described in the problem statement and that you must draw on your knowledge of the application domain and the real world to find them. If you have already used a method such as OOA you may already have identified the key attributes. Luckily attributes can (usually) be easily added to objects as and when they are identified - it is rare that the addition of a new attribute will cause the structure of the system to become unstable.

An important point to note is that you should only be trying to identify application domain attributes. This means that attributes which will be needed during the implementation of the system should not be included at this stage. However, link attributes (which might appear to be implementation attributes) should be identified at this stage as they have an impact on the systems structure. A link attribute is a property of the link between two objects, rather than being a property of an individual object. For example, the many to many association between Stockholder and Company has a link attribute of "number of shares".

Having identified the set of candidate attributes for each object you should then challenge these attributes using the following criteria:

**Should the attribute be an object?** For example, earlier we said that a telephone call should not be an object, however if you are constructing a telephone call billing system, then it is probable that it *should* be an object. You therefore need to think carefully about the domain when deciding whether something is an attribute or an object. Don't worry about getting it wrong (there may be no correct answer) you can come back to it later and refine the model.

**Is an attribute really a name?** Names are often actually selectors used to identify a unique object from a set of objects. In such situations a name should really be a qualifier.

**Is an attribute an identifier?** Here identifier relates to a computer based identifier and is an implementation issue and not part of the application domain. For example, `ObjectId` is an identifier which is probably not in the applications domain.

**Is an attribute really a link attribute?** Link attributes are often mistaken for object attributes. Link attributes are most easily identified when it becomes difficult to identify which of two (or more) classes the attribute should belong to. In such situations it is an attribute of the link between the two classes.

**Does the attribute represent an internal state of the object which is not visible outside the object?** If it does remove the attribute. It is an internal implementation issue and not part of the domain problem.

**Does the attribute represent fine detail?** If the attribute represents some aspect of the object which is relatively low level then omit it. It will not help with the overall understanding of the domain and will increase the complexity of the object model.

**Are any of the attributes unlike the others in their class?** Such discordant attributes may either be misplaced (this may indicate that one class should actually be two or more) or that the attribute is not part of the current application (although it may be part of the overall domain).

#### 19.3.2.5 Organize and simplify object classes using inheritance

The next step is to refine your classes using inheritance. This can be done in both directions, that is, either by grouping common aspects of existing classes into a superclass from which the existing classes inherit or by specializing an existing class into a number of more specialized subclasses which serve specific purposes. Again if you have used an analysis method such as OOA, some of this may already have been performed. Note that you are not doing this with a view to implementing the class hierarchy being generated, rather you are trying to understand the commonalities in the domain.

Identifying potential superclasses is easier than identifying specialized subclasses. To find potential superclasses, you should examine the existing classes looking for common attributes, operations or associations. Any common patterns you find may indicate the potential for a superclass. If such a situation is found, then the superclass should be defined with an appropriate name (i.e. one that encompasses the generic roles of the class which will inherit from it). Then move the attributes, associations and operations which are common up into this superclass. Do not try to force unrelated classes to become subclasses of a superclass just because they happen to have similar attributes (or associations or operations). Attempt to make sure that when you do group a set of classes together under a superclass, that grouping makes sense in the application domain. For example, grouping the classes
car, truck and bus under a superclass vehicle makes sense. However, adding the class student just because they all share the attribute *registrationNumber* does not make sense!

Identifying specializations can be more difficult, however if you find a class playing a number of specific roles then specialization may be appropriate. Note that you should be wary of specialization as you do not want to over specialize the classes in your object model. This is because you may actually be talking about separate instances of the same class, rather than subclasses.

### 19.3.2.6 Testing access paths

This step involves checking that paths in the model make sense, are sufficient and are necessary. O MT suggest that you trace access paths through the object model to see if they yield sensible results. The sorts of issues you might wish to consider are:

* Where a unique value is expected, is there a path yielding a unique result?

* For multiplicity “many” is there a way to pick out a unique value when needed?

* Are there any useful (domain specific/application specific) questions which can't be answered?

### 19.3.2.7 Iterate and refine the model

There are two things you will come to learn about object design, firstly it is still more of an art than a science and secondly (unless the problem is trivial) the first version of the object model will probably not be correct (or complete). Object oriented design is far more iterative in nature than some other design methods and it therefore acknowledges that you will need to iterate over the whole of the above process a number of times to get a reasonable object model. Indeed some changes will be initiated by the development of the dynamic model and the functional model which you have not even considered yet (for example we have not attempted to identify any operations for the object model at this point).

At this point some of the questions you can ask yourself about the object model are:

* Are there any missing objects (does one class play two roles)?

* Are there any unnecessary classes (such a class might possess no attributes)?

* Are there any missing associations (such as a missing access path for some operation)?

* Are there any unnecessary associations (such as those that are not used by anything)?

* Are all attributes in the correct class?

* Are all associations in the correct place?

Cross referencing the object model with the use case model may help answer some of the above questions.

### 19.3.2.8 Group classes into modules/packages

The final step associated directly with the object model is to group classes into packages. Packages should be identified by looking for classes which work together. Do not base the packages purely on system functionality as this is likely to change and will result in inappropriate packaging. OMT suggests that you ensure that a package can be fitted onto a single drawing surface (be that paper or the screen) as this aids comprehensibility. In addition, packages can be hierarchical and can be a very useful way of partitioning the design of the system amongst a number of designers.
# 20. More Object Modeling Technique

## 20.1 Introduction

This chapter continues from where the last chapter finished. It describes the dynamic and functional models of the analysis phase as well as the design and implementation phases.

## 20.2 Dynamic models

The dynamic models of the analysis phase capture the behavior of the system being analyzed. That is they describe what states the system can be in and what causes a state change to occur.

### 20.2.1 Dynamic modeling

The dynamic model describes the behavior of the application and the objects which comprise that application. The sequence, collaboration and state diagrams described in the last chapter are the main components of the dynamic model. OMT uses the term event trace to mean a sequence diagram and event flow to mean a collaboration diagram (in UML terms).

The aim of the dynamic model analysis is to identify the important events which occur and their effects on the state of the objects. Thus the first step in this phase is the identification of events and the objects associated with those events. Next the sequence in which those events occur must be identified which allows a state diagram to be drawn.

OMT recommends the following steps are performed in the construction of a dynamic model:

1. Prepare scenarios of typical interaction sequences.

2. Identify events between objects.

3. Prepare an event trace (sequence diagram and collaboration diagram) for each scenario.

4. Build a state diagram.

5. Match events between objects to verify consistency.

#### 20.2.1.1 Prepare scenarios

Scenarios illustrate the major interactions between the system and external actors on that system (whether human or otherwise). These scenarios are essentially the use cases in the use case model, if this has been performed. If not it is necessary to consider the different ways in which the system will be used and to determine the likely interactions. The scenarios can be written down as sequences of steps which describe one path through the systems. You should first prepare scenarios for the normal system interaction and then for exceptional system interaction.

#### 20.2.1.2 Identify events between objects

The scenarios essentially document external events between the system and the actor(s). These events should trigger internal events between the objects in the system. You should trace these events through the system, noting the objects involved and the types of events occurring. Having obtained sets of events you should group together events which have the same effect (even if they have different parameters). For example, an event to close a file has the same effect whichever file is being closed!
### 20.2.1.3 Prepare sequence and collaboration diagrams

Once you have identified events within the system document them first as sequence diagrams and then as collaboration diagrams. This approach is easier, as sequence diagrams tend to deal with the sequential ordering of the events from one object to another whereas a collaboration diagram may involve a number of objects.

### 20.2.1.4 Build a state diagram

A state diagram should be constructed for each object class with nontrivial dynamic behavior. Every sequence diagram (and thus collaboration diagram) corresponds to a path through a state diagram. Each branch in control flow is represented by a state with more than one exit transition. The procedure for producing state diagrams as described by the OMT method, is summarized below by the following algorithm:

1. Pick a class.

2. Pick one sequence diagram involving that class.

3. Follow the events for the class, the gaps in between the events are states. Give each state a name (if it is meaningful to do so).

4. Draw out a set of states and events linking states based on the sequence diagrams.

5. Now find loops within the diagram. That is, repeated sequences of states.

6. Choose another sequence diagram for the class and produce the states and events for that diagram. Next merge these states and events into the first diagram. That is, find the states and events which are the same and find where they diverge. Now add the new events and states.

7. Repeat step 6 for all sequence diagrams involving this class.

8. Repeat from step 1 for all classes.

After considering all normal events, add boundary cases and special cases. Also consider events which occur at awkward times including error events.

You should now consider any conditions on the transitions between states and any events which are triggered off by these transitions. Note that we still have not really considered the operations which the system will perform.

### 20.2.1.5 Match events between objects

Having produced the state diagrams you should now check for completeness and consistency across the whole system. Every event should have a sender and a receiver, all states should have a predecessor and a successor (even if they are start points or exit points) and every use case should have at least one state diagram which explains its effect on the system's behavior. You should also make sure that events which are the same but on different state charts have the same name.

## 20.2.2 Functional modeling

The functional model in OMT describes how values are computed. UML does not possess any notation for representing functional models and this reflects the lack of emphasis placed on functional model style analysis by object oriented design methods. OMT uses Data Flow Diagrams (or DFD's) to represent functional models. In essence the functional model explains how the operations (yet to be added) in the object model and the actions / activities of the dynamic model are achieved. It can also be used to represent constraints amongst the values of the model.

A DFD possesses inputs and outputs, data stores, processes and data flows (arrows). Figure 20.1 illustrates an example DFD diagram.

Data stores are passive objects which store data for later use. That is they merely respond to requests to store and access data. The Icon Definitions label between two parallel bars indicates a data store.

Processes are drawn as an ellipse. They possess a fixed number of inputs and outputs which are labeled with their type or name. Processes may be nested and of arbitrary complexity and eventually reference atomic operations.
*Data flows* are indicated by an arrow labeled with the type of data. They may split or converge. The tail of the arrow indicates the source and the head of the arrow the sink for the data.

*Actors* (drawn as rectangles) act as the eventual sources and sinks of the whole data flow. For example, the *screen buffer* is the eventual sink for the data flow in Figure 20.1.

<figure><img src="image_94.png" /><figcaption>Figure 20.1: A Data Flow Diagram</figcaption></figure>

OMT advises that it is best to construct the functional model after the object and dynamic models. The steps in this phase defined by OMT are:

1. Identify input and output values.

2. Build data flow diagrams showing functional dependencies.

3. Describe functions.

4. Identify constraints.

5. Specify optimization criteria.

### 20.2.2.1 Identify input and output values

Input and output values are parameters of events between the system and the outside world. You should reference the use case diagram to see whether the actors should provide information to the system or expect a response from the system.

### 20.2.2.2 Build data flow diagrams

The data flow diagrams are constructed by producing a diagram which groups inputs, processes those values and generates outputs. Each nontrivial process should then be broken down (at a lower level) into smaller steps. This process is repeated until only atomic operations remain. The result is a hierarchical model containing data flow diagrams which implement (higher level) processes.

Objects can also store data for use at a future date. These objects can be identified in a DFD because they receive values that do not result in immediate outputs but instead are used at some future time.

### 20.2.2.3 Describe functions

Once the DFD for a particular function has been defined you should write a description of that function. This description can be in the form of natural language, pseudo code, mathematical equations, decision tables or any other suitable form. The intention is that the function description places the DFD (which indicates how data is processed) into context.

### 20.2.2.4 Identify constraints

Constraints are functional dependencies between objects. They may be preconditions or postconditions on a function or a relationship which must hold (for example `birthDate < currentDate`). They may exist between two instances or between different instances at different times. Either way, they need to be documented.
### 20.2.2.5 Specify optimization criteria

Specify any data values which should be maximized (for example, process as many orders in an hour as possible), minimized (for example, ensure that system response is < 2 milliseconds) or otherwise optimized.

## 20.2.3 Adding operations

It is only at this point that the operations in the analysis models are considered. Note how this differs from the procedural approach in which the operations to be performed would be considered right *up front*. The operations are summarized by the object model (but related to functions, actions and events in the functional and dynamic models). The criteria used for identifying operations are:

* operations implied by events,

* operations implied by state actions and activities,

* operations from functions,

* application / domain operations,

* simplifying operations.

### 20.2.3.1 Operations implied by events

All the events in the object model correspond to operations (although a single operation may handle multiple events and vice versa). OMT suggests that during analysis "events are best represented as labels on state transitions and should not be explicitly listed in the object model". However, if you find it clearer to list the operations corresponding to the events in the object model; then do so.

### 20.2.3.2 Operations implied by state actions and activities

The actions and activities in the state diagrams correspond to operations. These can be listed in the corresponding classes in the object model.

### 20.2.3.3 Operations from functions

Each function corresponds to one or more operations. The functions should be organized into operations on objects. This is not as straight forward as it might at first seem since we have not yet associated the functions with objects.

### 20.2.3.4 Domain operations

There may be additional domain operations which are not immediately obvious from the problem description. These should be identified from additional domain knowledge and noted. For example, although a cash point system (ATM system) does not allow you to open and close accounts, such operations are appropriate within the domain and may be important for understanding the domain or for aspects of the application which have yet to come to light.

### 20.2.3.5 Simplifying operations

Examine the object model for operations which are essentially the same. Replace these operations with a generic one. Note that earlier steps may well have generated the same operation but with different names. Therefore check each object's operations to see if they are intended to do the same thing even if they have very different names. Adjust the other models as appropriate.
## 20.3 Design phase

The aim of the analysis phase is to identify what needs to be done and not how it is done. The design phase takes the models produced by the analysis and considers how the requirements can be achieved. It is broken down into two sub-phases. The first, called the system design, breaks the overall system down into components called subsystems, while the second, called the object design, moves the models produced towards the implementation.

### 20.3.1 System design phase

The subsystem architecture provides the context within which the more detailed design decisions, made during the object design, are performed. The subsystem decomposition defines an architecture which can be used as the basis by which the detailed design can be partitioned among a number of designers, thus allowing different designers to work independently on different subsystems. The steps used to generate this architecture are:

1. Organizing the system into subsystems.

2. Identifying concurrency inherent in the problem.

3. Allocating subsystems to processors and tasks.

4. Choosing an approach for management of data stores.

5. Handling access to global resources.

6. Choosing the implementation of control in software.

7. Handling boundary conditions.

8. Setting trade offs between competing priorities.

Of course not all these steps will be important for all applications. For example, a batch oriented, purely serial process, probably cannot have much concurrency imposed on it.

#### 20.3.1.1 Breaking the system into subsystems/packages

Most systems will be comprised of a number of subsystems, for example, a payroll system might possess a file subsystem, a calculation subsystem and a printing subsystem. A subsystem is not an object nor a function, but a package of classes, associations, operations, events and constraints that are interrelated and that have a reasonably well-defined and (hopefully) small interface with other subsystems. The package notation in the UML can be used to represent subsystems.

A subsystem (or package) is usually characterized by the common (or associated) set of services that it provides. For example, the file package would provide a set of services to do with creating, deleting, opening, reading and writing files. The use case model may be useful in identifying such common services.

Each package therefore provides a well defined interface to the remainder of the system which allows other packages to use its facilities. Such a specified interface also allows the internals of the package to be defined independently of the rest of the system (i.e. it encapsulates the package). In addition, there should be little or no interactions between objects within the package and objects in another package (except via the specified interfaces).

Packages can be hierarchical and may be involved in *client-server* or *peer to peer* relationships with other packages. Client-server relationships are easiest to implement and maintain as one package responds to requests from another package and returns results. In peer to peer relationships both packages must be capable of responding to requests from the other. This can result in unforeseen circularities.

#### 20.3.1.2 Identifying concurrency

Concurrency can be very important for improving the efficiency of a system. However, to take full advantage of concurrency the system must be designed around the concurrency inherent in the application. This can be done by examining the dynamic model for objects which receive events at the same time or perform any state transitions (and associated actions) without interacting. Such transitions
are therefore concurrent and can be placed in separate execution threads without effecting the operation
of the system.

### 20.3.1.3 Allocating subsystems to processors and tasks

Each concurrent package should be allocated to an independent process or processor. The system
designer must therefore:

* estimate performance needs and the resources needed to satisfy them,

* choose hardware or software implementations for packages,

* allocate packages to processors to satisfy performance needs and minimize inter processor communication,

* determine the connectivity of the physical units that implements the packages.

A deployment diagram can be used to illustrate the results of this step.

### 20.3.1.4 Management of data stores

The designer must identify appropriate data stores for both internal and external data. This involves
identifying the complexity of the data, the size of the data, the type of access to the data (single or
multiple users), access times and portability. Having identified these issues decisions can be made about
whether data can be held in internal memory, on secondary storage devices, whether it should be held in
flat files, relational or object database system.

### 20.3.1.5 Handling access to global resources

The system designer must identify what global resources are required and how access to them can be
controlled. Global resources include processors, disk drives, disk space, workstations as well as files,
classes and databases.

### 20.3.1.6 Choosing the implementation of control in software

The choice of the internal control mechanism used by the system will be mediated by the facilities
provided by the implementation language. For example, Ada supports concurrent tasks whereas
VisualBasic does not. Smalltalk and Java support light weight processes and therefore can be said to
mimic concurrent systems. The choices available are:

* *procedure oriented systems*. Such a system represents a procedure calling mechanism in which the flow of control is passed from one procedure / method to another when the first calls the second.

* *event driven systems*. This is the approach taken by the dynamic model of the analysis phase. Essentially operations are triggered off by events which are received by objects. Many window based interfaces operate in this manner.

* *concurrent systems*. In which the system exists in several processes which execute at the same time. Some synchronization between the processes may take place at certain times, but for the majority of the time they are completely separate.

### 20.3.1.7 Handling boundary conditions

There are three primary boundary situations which the designer should consider. These are:
initialization, termination and failure. Initialization involves setting the system into an appropriate,
clean, steady state. Termination involves ensuring that the system shuts down in an appropriate
manner. Failure involves dealing with unplanned termination of the system cleanly.
### 20.3.1.8 Setting trade offs between competing resources

In any design there are various trade offs to be made. For example, the trade off between speed of access and data storage is a common one in database systems. The larger the number of indexes used, the faster data retrieval can be made (however the indexes must now be stored along with the data). Such design trade offs must be made with regard to the system as a whole (including non -software issues) as sub-optimal decision will be made if they are left for designers concentrating on a single package.

## 20.3.2 Object design phase

The object design essentially takes the models produced by the analysis phase and fleshes them out ready for the implementation. Thus the objects identified during the analysis act as the skeletons for the design. However, the designer must now consider how the analysis objects (partitioned into packages) should be implemented. They must express the operations identified earlier in terms of algorithms which can be implemented and associations as appropriate references from one object to another (taking into account the type of facilities provided by the target language). New classes may need to be introduced to deal with aspects which are important for the design (and ultimately the implementation) but are not significant for the analysis.

The following steps are performed by the designer during the object design:

1. Combine the three models to obtain operations on the classes.

2. Design algorithms to implement operations.

3. Optimize access paths to data.

4. Implement control for external interactions.

5. Adjust class structure to increase inheritance.

6. Design associations.

7. Determine object representation.

8. Package classes and associations into modules.

As stated before, these steps are iterative.

### 20.3.2.1 Combining the three models

If the implied operations from the dynamic and functional models have not been added to the object model, then they should be added now.

### 20.3.2.2 Designing algorithms

Each of the DFDs in the functional model needs to be expressed as an algorithm to indicate how they perform the function. The DFD says what it does, but the algorithm says how it does it. The algorithm designer must:

* Choose algorithms that minimize the cost of implementing operations.

* Select data structures appropriate to the algorithms.

* Define new internal classes and operations as necessary.

* Assign responsibility for operations to appropriate classes.

Note that any algorithms defined in pseudo code during the analysis phase were intended to explain the required functionality. It is therefore necessary at this stage to consider what algorithms are required in the implementation. For example, although a bubble sort algorithm may have been used in the analysis phase, an insertion sort may well be a more efficient algorithm to choose for the design.
### 20.3.2.3 Design optimization

The analysis model was only intended to describe the application and its requirements and did not attempt to take into account efficient information access or processing. These issues need to be considered by the designer at this point. In particular they should consider:

* adding redundant associations to minimize access cost and maximize convenience,

* rearranging the computation for greater efficiency,

* saving derived attributes to avoid recomputation of complicated expressions.

### 20.3.2.4 Implementation of control

During the system design, an approach for handling the internal control of the system must have been identified. That approach is fleshed out here. This includes determining how the selected approach can be implemented and identifying any constraints this choice imposes on the design.

### 20.3.2.5 Adjust class structure

As the design progresses the class hierarchy is likely to change, evolve and become refined. It is quite common to produce a design and then to rearrange it in light of commonalities which were hidden at an earlier stage. The designer should:

* rearrange and adjust classes and operations to increase inheritance,

* abstract common behavior out of groups of classes,

* use delegation to share behavior when inheritance is semantically invalid.

### 20.3.2.6 Design associations

Associations are an important aspect of the analysis object model. However, they are conceptual relationships and not implementation oriented relationships. The designer needs to consider how the associations can be implemented in a given language. The choices made for representing associations may be made globally for the whole system, locally to a package or on an association by association basis. The criteria used for determining how associations should be represented in the design are based on how are they traversed. If they are traversed only in one direction then a pointer representation may be sufficient. However, if they are bi-directional then an intermediate object may best represent the association.

### 20.3.2.7 Determine object representation

In most situations it is relatively straight forward to identify how to represent an object. However in some cases consideration needs to be given to decide whether to use a system primitive or an object. For example, in Java there are basic types int and char and there are also classes `Integer` and `Character`.

### 20.3.2.8 Package classes

The system will have been decomposed into *logical* packages in the system design. However, different languages provide different facilities for *physically* packaging a system. VisualWorks provides class categories which can be used in a similar manner to packages but they do not provide any information hiding etc.

## 20.4 Implementation phase

OMT states that implementation is "an extension of the design process" and that "writing code should be straightforward, almost mechanical, because all the difficult decisions should already have been
made”. (However implementation still tends to possess unexpected desi gn problems which must be solved. These decisions should be subject to, and determined by, the processes described above). Because of this, OMT places a limited amount of emphasis on the implementation phase, concentrating instead on stylistic points. You should treat the implementation of an object oriented system in just the same way as you would treat the implementation of any software system. This means that it should be subject to, and controlled by, the same processes as any other implementation. In a ddition it should be subjected to similar testing (as discussed earlier in this book). This is where the use cases may come back into use in helping identifying suitable test scenarios.

## 20.5 Summary

As can be seen from this chapter, OMT concentrates the majorit y of its guidance on the analysis phase, the design and implementation phases are far less well supported. It is however one of the most widely used object oriented design methods and is likely to have a very large influence on any method developed by Booch, Rumbaugh and Jacobson to support the UML.
# 21. Frameworks and Patterns for Object Oriented Design

## 21.1 Introduction

Designing complex software systems is hard. It is a great deal easier to reuse an existing software system, merely modifying it where necessary, than to build it from scratch. These two facts have led to a great deal of interest in what has become termed software frameworks in the object oriented community. A software framework is "the reusable design of a system or a part of a system expressed as a set of abstract classes [and concrete classes] and the way instances of (subclasses of) those classes collaborate" [Beck and Johnson 1994]. However such frameworks are notoriously difficult to document. For example the Model View Controller (MVC) framework in Smalltalk is very powerful, but it has proved difficult to explain in a clear and simple manner how the MVC should be used [Krasner and Pope 1988].

In the object oriented community a number of researchers have explored the work of an architect who designed a language for encoding knowledge of the design and construction of buildings [Alexander et al 1977, Alexander 1979]. The knowledge is described in terms of patterns which capture both a recurring architectural arrangement and a rule for how and when to apply this knowledge. That is, they incorporate knowledge about the design as well as the basic design relations.

The result is that there is now a growing community exploring how software frameworks can be documented using (software) design patterns (for example, [Johnson 1992] and [Birrer and Eggenschmiler 1993]). Johnson's paper describes the form that these design patterns take and the problems encountered in applying them.

The remainder of the chapter is structured in the following manner: Section two considers what a framework is and the role of patterns in documenting frameworks. Section three introduces the HotDraw framework and Section four presents a very simple pattern documenting how you can use HotDraw to construct a simple editor.

## 21.2 Patterns and frameworks

### 21.2.1 What is a framework?

A framework is a reusable design of a program or a part of a program expressed as a set of classes. That is, a framework is a set of prefabricated software building blocks that programmers can use, extend, or customize for specific computing solutions. With frameworks, software developers don't have to start from scratch each time they write an application. Frameworks are built from a collection of objects, so both the design and code of a framework may be reused. However frameworks are not necessarily easy to design or implement. Questions such as "how much will it cost" to produce a framework and "how much will we benefit" from the network are difficult to answer [Moser and Nierstrasz 1996]. In addition reusing frameworks instead of libraries can cause subtle architectural changes in an application calling for innovative management [Sparks, Benner and Faris, 1996].

Frameworks can provide solutions to different types of problem domain. These include application frameworks, domain frameworks and support frameworks:

* **Application frameworks** encapsulate expertise applicable to a wide variety of programs. These frameworks encompass a horizontal slice of functionality that can be applied across client domains. An application framework might provide the basic facilities of a payroll system, or a
geographic information system. These facilities could then be used to construct a concrete payroll (or GIS) system with the features required by a specific organization.

* **Domain frameworks** encapsulate expertise in a particular problem domain. These frameworks encompass a vertical slice of functionality for a particular client domain. Examples of domain frameworks include: a control systems framework for developing control applications for manufacturing systems or drawing frameworks such as HotDraw, Unidraw for C++/X windows and DRAW_Master for C++/OS 2 and Windows environments.

* **Support frameworks** provide system-level services, such as file access, distributed computing support, or device drivers. Application developers typically use support frameworks directly or use modifications produced by system providers. However, even support frameworks can be customized - for example when developing a new file system or device driver.

Since frameworks are reusable designs, not just code, they are more abstract than most software which makes documenting them more difficult. Documentation for a framework has three purposes and patterns can help to fulfill each of them. Documentation must provide:

1. the purpose of the framework,

2. how to use the framework,

3. the detailed design of the framework.

### 21.2.2 Designing object-oriented frameworks

The process of designing and implementing frameworks is not trivial and is, in many ways, harder than designing and implementing a one off application. This is because the framework needs to be complete, robust, generic, easy to use, extensible and flexible if it is to be effective:

* **Complete.** A framework must provide all the infrastructure required to construct applications. This includes documentation, concrete examples as well as the underlying skeleton of the framework. If any of these are missing (in particular the underlying skeleton) then this will act as an impediment to the adoption of the framework.

* **Robust.** A framework that is not robust will soon be ignored as users will not want to have to debug the framework as well as their own code.

* **Generic.** A framework that is not generic (relative to its application area) will be difficult to apply except in very specific ways. This can reduce the incentive for organizations to adopt a framework.

* **Easy to use.** A framework which is difficult to use will not be adopted as few developers have the time to gain the necessary understanding to take advantage of such a framework.

* **Extensible.** Users of the framework should find it easy to add and modify default functionality. That is, the framework should provide hooks so that users can apply the framework in different ways.

* **Flexible.** One of the claimed benefits of frameworks is that they can be applied and used in ways which were not envisaged by their creators. They therefore need to be flexible.

The first step in developing a framework is to analyze the application problem domain such that you know the requirements of any frameworks, the types of frameworks that would be useful and how they might be used. For example, look for the types of subsystems which are built repeatedly. A pseudo natural language parser might be a common subsystem within a range of diagnostic tools.

The next step involves identifying what is common about these systems and what is different. This allows the identification of the primary abstractions for the framework. These are the components which are constant across applications (even though they may have different names in these different applications). Note that it is often easiest to work bottom up in identifying these abstractions (in some cases the designs of the system might be the best starting points and in others the source code).

The third step involves taking these abstractions and constructing a skeleton for the framework. This skeleton should provide the basic infrastructure (comprised of the abstract concepts) for the framework. This is a bit like attempting to produce a set of abstract classes and their interactions for some application, without filling out all (or most) of the concrete classes.
As the framework takes shape you should be continually attempting to refine it by adding more default behavior, more hooks into it as well as additional ways for users to interact with it. You will also need to produce concrete instantiations of the framework to test its functionality and to customizability. These example instantiations should be included with the framework as part of the delivered product (whether it is delivered internally or externally to your organization).

The whole development process is extremely iterative but is by necessity subject to the strictest of software engineering practice. In addition suitable documentation of the framework is essential (and this is where design patterns come into their own) from design, implementation and user perspectives.

Finally, some useful general comments about framework development include:

* Develop frameworks by examining existing applications.

* Develop small focused frameworks (breaking down larger frameworks when the opportunity to do so is identified).

* Build frameworks using an iterative process prototyping how the frameworks will be used all the time.

* Always look out for additional functionality for the framework. For such functionality, determine whether it should be a default behavior of the framework or whether a hook should be provided to allow a user to implement it.

* Good documentation is essential and should include sample instantiations of the framework, descriptions of the framework architecture, descriptions of the framework and its intent, guidelines on using the framework and cookbook examples for particular operations.

### 21.2.3 What is a design pattern?

Design patterns capture expertise in building object oriented software [Gamma et al 1993; Johnson 1992; Beck and Johnson 1994]. A design pattern describes a solution to a recurring design problem. It also contains information on the applicability of a pattern, the trade offs which must be made and any consequences of the solution. Books are now appearing which present such design patterns for a range of applications. For example, [Gamma et al, 1995] is a widely cited book which presents a catalog of 23 design patterns. Design patterns are extremely useful for both novice and experienced object oriented designers. This is because they encapsulate extensive design knowledge and proven design solutions with guidance on how to use them. Reusing common patterns opens up an additional level of design reuse, where the implementations vary, but the micro-architectures represented by the patterns still apply.

There are potentially very many design patterns available to a designer. A number of these patterns may superficially appear to suit their requirements, even if the design patterns are available on -line (via some hyper text style browser) it is still necessary for the designer to search through them manually, attempting to identify the design which best matches their requirements.

In addition, once they have found the design which they feel best matches their needs, they must then consider how to apply it to their application. This is because a design pattern describes a solution to a particular design problem. This solution may include multiple trade offs which are contradictory and which the designer must choose between, although some aspects of the system structure can be varied independently (although some attempts have been made to automate this process for example [Budinsky et al 1996]).

Patterns seem to be exceptionally well suited for documenting frameworks. They certainly provide guidance on how to use a framework, the purpose of the framework, they can also include concrete examples and can be used to document the design of the framework.

### 21.2.4 Pattern templates

The pattern template used in [Gamma et al, 1995] provides a standard structure for the information which comprises a design pattern. This makes it easier to comprehend a design pattern as well as providing a concrete structure for those defining new patterns. Gamma's book [Gamma et al, 1995] provides a detailed description of the template; only a summary of it is presented in Table 21.1.

Patterns are best suited for teaching how to use a framework, but with care they can meet all three purposes identified earlier. By describing the design of a framework in terms of patterns, you describe both the design and the rationale behind the design. As patterns also show how to use the framework for
a particular problem, the user gains a better understanding of the framework as a whole. It is essentially a tutorial on using a framework, but one which is more than a cookbook.

The problem with cookbooks is that they describe a single way in which the framework will be used. A good framework will be used in ways that its designers never conceived. Thus a cookbook is insufficient on its own to describe every use of the framework. Of course a develope r's first use of a framework usually fits the stereotypes in the cookbook. However, once they go beyond the examples in the cookbook, they need to understand the details of the framework. The problem is that cookbooks tend not to describe the framework its elf. But in order to understand a framework, you need to have knowledge of both its design and its use. Patterns provide opportunities for describing both the design and the use of the framework as well as including examples, all within a coherent whole. I n some ways patterns act like a hyper -graph with links between parts of patterns. To illustrate the ideas behind frameworks and patterns the next section will present the framework HotDraw and a tutorial HotDraw pattern example explaining how to construct a simple drawing tool.

<table><caption>Table 21.1: The design pattern template</caption><thead><tr><td>Heading</td><td>Usage</td></tr></thead><tbody><tr><td>Name</td><td>The name of the pattern</td></tr><tr><td>Intent</td><td>This is a short statement indicating the purpose of the pattern. It includes information on its rationale, intent, problem it addresses etc.</td></tr><tr><td>Also known as</td><td>Any other names by which the pattern is known.</td></tr><tr><td>Motivation</td><td>Illustrates how the pattern can be used to solve a particular problem.</td></tr><tr><td>Applicability</td><td>This describes the situation in which the pattern is applicable. It may also say when the pattern is not applicable.</td></tr><tr><td>Structure</td><td>This is a (graphical) description of the classes in the pattern.</td></tr><tr><td>Participants</td><td>The classes and objects involved in the design and their responsibilities.</td></tr><tr><td>Collaborations</td><td>This describes how the classes and objects work together.</td></tr><tr><td>Consequences</td><td>How does the pattern achieve its objective? What are the trade offs and results of using the pattern? What aspect of the system structure does it let you vary independently.</td></tr><tr><td>Implementation</td><td>What issues are there in implementing the design pattern.</td></tr><tr><td>Sample Code</td><td>Code illustrating how a pattern might be implemented.</td></tr><tr><td>Known uses</td><td>How the pattern has been used in the past. Each pattern has at least two such examples.</td></tr><tr><td>Related patterns</td><td>Closely related design patterns are listed here.</td></tr></tbody></table>

## 21.3 An introduction to HotDraw

What is HotDraw? HotDraw is a drawing framework developed by Ralph Johnson at the University of Illinois at Urbana-Champaign [Johnson 1992]. It is a reusable design for a drawing tool expressed as a set of classes. However, it is more than just a set of classes; it possesses the whole structure of a drawing tool, which only needs to be parameterized to create a new drawing tool. It can therefore be viewed as a basic drawing t ool and a set of examples which can be used to help you develop your own drawing editor!

Essentially HotDraw is a skeleton DrawingEditor waiting for you to fill out the specific details. That is, all the elements of a drawing editor are provided including a basic working editor., which you, as a developer, customize as required. What this means to you is that you get a working system much, much sooner and with a great deal less effort.

### 21.3.1 The HotDraw framework

HotDraw was first presented at the OOPSLA'92 conf erence in a paper entitled "Documenting Frameworks using Patterns" by Ralph Johnson [Johnson 1992]. This paper is somewhat abstract, however the appendices included with the paper are very useful as guides for changing the default drawing editor. The concept behind frameworks is that a set of abstract classes are only so useful, it is much more useful to capture the design in the classes along with concrete examples. The combination of design information, concrete examples and an explicit skeleton to work with is easier to understand. Indeed it is usually the case that concrete examples to follow and modify are a much better way to learn
about a set of classes, than to attempt to understand the class and work from there (particularly when you are learning Smalltalk). For example, if a method takes a block as an argument, what is that block supposed to look like? An example or two are often invaluable.

Every HotDraw application is comprised of a number of elements, these are the Drawing Editor, the Drawing View, the Drawing Controller, the Drawing, figures in the drawing, handles, constraints between drawings and creation tools. Each of these is explained briefly below:

**DrawingEditor** *DrawingEditor* is a subclass of *Model* that represents a graphical drawing editor. It is the model of the MVC triad that includes a *DrawingView* and a *DrawingController*. It shares the *DrawingView* and the *DrawingController* with a *Drawing*.

**DrawingView** A *DrawingView* is the view component of a MVC triad. The *Drawing* and the *DrawingController* are the other components. A *DrawingEditor* also uses the *DrawingView* in a MVC triad.

**DrawingController** A *DrawingController* is the controller component of a pair of MVC triads. A *Drawing* and a *DrawingView* is one pair and a *DrawingEditor* and *DrawingView* are the other pair. A *DrawingController*'s primary task is to delegate mouse and keyboard activity to the current tool of the *Drawing*.

**Drawing** A drawing is a directed graph of figures (or complex figures). As indicated above it is a model in a MVC triad with the *DrawingView* and *DrawingController*.

**Figures** A *Figure* is a kind of drawing element or widget which describes how it should be drawn within a *DrawingEditor*. A *CompositeFigure* is a figure which can contain other figures as components, which all have an *influence* on what the composite figure looks like. *CompositeFigure* also defines a bounding box which is independent of its sub-components. Only components which are wholly within the bounding box are actually displayed. Unlike *VisualComponents* in VisualWorks, figures keep track of which objects which depend on them.

**Handles** All figures can potentially have handles. Handles are a way to modify a figure's attributes, for example by resizing the figure, or by linking a figure to another figure and thereby creating a dependency. The *Figure* class defines a method handles which returns a basic set of handles for resizing a figure. It is therefore common for subclasses of figure to redefine the handles method, either to overwrite this, or to add to it (in which case the method would "call super" handles at some point).

**Constraints** HotDraw provides *Constraint* objects which are used to express dependencies between objects. A *MultiheadedConstraint*, which is a subclass of *constraint*, makes the state of one object the function of the states of many other objects. The one object is the "sink" and the many objects are the "sources". For example, if the value of one cell in a spreadsheet is the sum of five other cells, then the five cells are the sources and the cell with the sum is the sink.

**CreationTools** These are used to create a new instance of a figure. They are parameterized by the class of the figure to display, the icon to display in the tool palette and the cursor to use while that tool is selected.

### 21.3.2 Obtaining HotDraw

HotDraw is available by anonymous FTP from the Smalltalk repository at st.cs.uiuc. There are versions available for most of the recent releases of ParcPlace Smalltalk (e.g. VisualWorks version 1.0, 2.0 and 2.5). You should follow the installation instructions and then immediately save the image onto your local file store and exit VisualWorks.

### 21.3.3 Examples with HotDraw

The examples provided with HotDraw include a Pert Chart tool, a HyperCard clone, HotPaint and Network Editor which allows you to play with nodes in a network which are either attracted or repulsed by each other and a diagramming inspector. If you have access to HotDraw (e.g. via ftp) then explore them! Explore the patterns at the end of this chapter which describes the patterns based on one of these example applications.
Finally, it is interesting to note a comment made in Ralph Johnson's paper "The implementation [of HotDraw for ObjectWorks Rel 4.0] was simpler than previous ones because the design of the user interface framework for release 4.0 was influenced by HotDraw".

### 21.3.4 Facilities provided by HotDraw

So what does HotDraw provide in this framework. (I will use the term framework to describe the basic HotDraw editor). You get the following facilities:

* A *Tool palette view*. This is the tool bar down the left hand side of the display.

* A *Drawing View*. This is the area a user would actually draw in.

* A *Drawing Editor*. This is the model for the Drawing View and Drawing Controller.

* A *Drawing*. This is also a model used by the Drawing View and Drawing Controller.

* A *Drawing Controller*. which allows you to interact with the application.

All these are already connected together in the appropriate manner, unlike many class-only packages, in which you as the developer must determine how to bring these elements together.

## 21.4 Where do you start?

<figure><img src="image_95.png" /><figcaption>Figure 21.1: The BoxDraw application to be built</figcaption></figure>

The remainder of this document is in effect a sample pattern which illustrates how you create a simple extension to the drawing editor. The problem to be addressed by this pattern is:

> The basic facilities provided by HotDraw make it very easy to construct a simple graphic editor for drawing diagrams. One useful feature of such diagrams is the ability to link figures together and to get those links to move when the figures are moved. Thus there needs to be a way to link two figures together.

### 21.4.1 The DrawingEditor

You will almost always start by subclassing the DrawingEditor. This is the basic element of any HotDraw tool. In this case we shall subclass DrawingEditor to create BoxDrawEditor, which will be the basis of our application BoxDraw. The definition of the BoxDrawEditor is presented below:

```
DrawingEditor subclass: #BoxDrawEditor
```
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'BoxDraw'

Note that I have put this class in a category called BoxDraw. Also note that I have not had to define any new instance or class variables - it is not necessary as I inherit all I need from DrawingEditor. In fact I am not even going to define any instance methods for this class. So why have a BoxDrawEditor class at all? Because I want a different set of tools in the tool Palette.

### 21.4.2 Define the set of tools to use

The next step is therefore to define the set of tools available to a user of BoxDraw. This is done by defining a method called `defaultTools` on the class side. This method is used to create the `Tool palette` view. This method returns an `OrderedCollection` of `creationTools`. Every type of figure will provide a creation tool for example:

`LineFigure creationTool.`

We are going to use the default set of tools, with the addition of just one figure, called `BoxDrawFigure`. We can therefore use the `defaultTools` method defined in the superclass of `BoxDrawEditor` and just add a creation tool for `BoxDrawFigure` to the end of this `OrderedCollection`. The code for this method is listed below:

```java
defaultTools
    "Answer an OrderedCollection of the tools I can use"
   ^(super defaultTools) add: (BoxDrawFigure creationTool); yourself.
```

This method should be defined in a class method category called `defaults`.

### 21.4.3 An example class method

Finally, for the `BoxDrawEditor` class we shall define a class method protocol called `examples`. In this protocol we shall define a single method called `example1`. This method will send the message open to the `BoxDrawEditor` which will create a new instance of the `BoxDrawEditor` and will cause the graphic editor we are constructing to be displayed.

`example1`
`BoxDrawEditor open.`

## 21.5 BoxDrawFigure

### 21.5.1 Defining the figure

We now have a drawing editor for BoxDraw with everything we need defined - except what a `BoxDrawFigure` is. This is the next step.

We could make a `BoxDrawFigure` a subclass of a simple figure such as `Rectangle`, `Ellipse` or `PolylineFigure`. However, we want to have a text field in the middle of `BoxDrawFigure` which will represent the name of the figure. This means that `BoxDrawFigure` will have at least one subfigure, we therefore want to make `BoxDrawFigure` a subclass of `CompositeFigure`.

A `CompositeFigure` is a figure which possesses subfigures, who determine part or all of the look of the figure. `CompositeFigures` are also often used if it is necessary to give a user access to the subfigures (for example to change the text in the text field of the `BoxDrawFigure`). The definition for `BoxDrawFigure` is given below:

```java
CompositeFigure subclass: #BoxDrawFigure
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'BoxDraw'
```
### 21.5.2 Adding an instance creation method

Now we have a class called `BoxDrawFigure`, we need some way of instantiating this class. HotDraw expects a figure's instance creation method to be called `createAt:` (rather than `new` or `open` with which you may be more familiar). We will therefore define a method `createAt:` in the protocol "instance creation":

```kotlin
createAt: aPoint
^self new initializeAt: aPoint
```

This method first creates a new instance of the `BoxDrawFigure` and then sends it an `initializeAt:` message with `aPoint` as the current cursor position. The `initializeAt:` method will actually draw the figure and its component elements. We will look at this method below.

### 21.5.3 Defining a creation tool

While we are still on the class side, we shall define the `creationTool` method which specifies what icon to use in the `Tool Palette` for a `BoxDrawFigure`. Rather than define a new `creationTool` class, we will provide a `creationTool` method which will return the default creationTool, parameterized for `BoxDrawFigure`. This method will be defined in the class protocol "tool creation". The `creationTool` method is:

```kotlin
creationTool
^Tool
  icon: (Image
    extent: 16 @ 16
    depth: 1
    palette: MappedPalete blackWhite
    bits: #[255 255 255 255 255 255 128 1
           191 253 160 125 191 253 160 197
           191 253 160 125 191 253 28 1
           255 255 255 255 255 255 255 255]
    pad: 8)
    cursor: Cursor crossHair
class: self
creationMessage: #createAt:
```

This method is based on that defined in `PERTEvent` in one of the `HotDraw` example applications. It returns an instance of `CreationTool` which uses a particular icon on class `BoxDrawFigure` and uses a `crossHair` (rather than `origin`) style cursor. Note that the `creationMessage` is specified here. We could have used a different creation message to that used elsewhere in `HotDraw`. However, to remain consistent with existing `HotDraw` applications we used `createAt:`.

### 21.5.4 The initializeAt: method

We now need to define what the elements of this drawing will be. That is, "what the figure will look like". This is done within `initializeAt:` defined in "initialize" protocol on the instance side of the class. The code for this method is:

```kotlin
initializeAt: aPoint

| title myFigures aRectangle |
"Get the bounding rectangle"
aRectangle := aPoint extent: 90 @ 45.
origin := aRectangle origin.
"Position the text field relative to the origin"
title := FixedTextFigure string: 'A Name' at: origin + (5 @ 5).
title Longer: 65.
"Add the text field to the subelements of this figure"
myFigures := (OrderedCollection new) add: title; yourself.
self setFigures: myFigures visibleArea: aRectangle.
"Draw the bounding box of this figure"
self showVisibleAreaIndicator.
```

This method first creates a rectangle whose right hand corner is indicated by `aPoint` and whose extent is 90 by 45. Having done that, it then sets the `origin` of the figure relative to the rectangle. Next it creates a text field at a particular point and then uses the bounding box of the figure to draw the shape of the figure (in this case a rectangle).
### 21.5.5 Running the editor

You have now done enough to open a BoxDrawEditor. Do this by evaluating:

```
BoxDrawEditor open.
```

in a Workspace window.

The BoxDrawFigure is the last icon in the Tool Palette. However, all you can do is place a BoxDrawFigure in a drawing and change the label. Note to change the label, select the Text tool (the big 'T') and hold down the shift button while selecting the label with the left mouse button.

### 21.5.6 Adding handles

To be able to link a BoxDrawFigure to another figure we need to add another handle. Handles provide a way of telling a figure that so me operation should be performed. The most common handles are for shrinking or growing a figure. For example, handles on the four corners of a figure. We want to add another handle which will allow us to link one BoxDrawFigure to another BoxDrawFigure.

By default we have inherited the handles defined in the figure. These handles are on the four corners of our figure and allow the user to resize the figure. The method `handles` in the protocol "accessing" on the class `Figure` provides for this. We want to add an additional handle which allows links to be created. We shall select the center point. However we do not want to lose the ability to resize our figures, we shall therefore define a new handles method in the "accessing" protocol of the BoxDrawFigure which uses `super` to obtain the original handles and then add our handle to this collection of handles:

#### handles

```
"Add a handle at my center that draws connections to other figures"
^(super handles) add: (CommandHandle
    connectionFor: self
    at: #center:
    class: LineFigure); yourself
```

We now have a fully functional BoxDraw application. Open the BoxDrawEditor again and place some BoxDrawFigures on the drawing area. Now select the "arrow" icon on the icon menu. This is the top icon. Next select one of the BoxDrawFigures. You should see a small square (a handle) in the center of the figure. Click on this and drag the resulting line to another BoxDrawFigure. This should connect the two figures together. Now move the figures around. You should find that the line between the two figures moves with you.

### 21.5.7 Changing a figure menu

To complete this application we shall change the menu available when clicking on a BoxDrawFigure using the middle mouse button. At present the user has the option to show visible area, hide *v*isible area and reset visible area as well as the standard cut, copy and paste options. We only want the user to have access to the standard cut, copy and paste options. To do this we redefine the menu method inherited from CompositeFigure. This method is defined within the accessing protocol:

#### menu

```
"Define a pop up Menu for use with BoxDrawFigure"
^Menu labels: 'cut\copy\paste' withCRs
    values: #(#cut #copy #paste )
```

### 21.5.8 The (pattern) solution statement

The ability to specify handles which perform specific functions allows operations to be performed through operation of the mouse. The use of a CommandHandle with a LineFigure can be used to link two figures together.
## 21.6 Summary

In this chapter we explored the concepts of frameworks and patterns as a method of documenting frameworks. We have briefly considered what we mean by frameworks and how frameworks can be developed. We then discussed what a pattern is and how they may be used to document a framework. Following on from this an extended tutorial describing how the framework HotDraw can be used to develop a simple drawing tool called BoxDraw. This tutorial example was presented in the manner of a very (simplified) pattern.
Part Four

**Testing and Style**
## 22. Testing Object Oriented Systems

### 22.1 Introduction

Testing object oriented systems is a very important issue as more and more organizations are starting to develop Smalltalk (as well as C++ and Java) based applications. Many such organizations have been forced to come up with their own solutions for assuring the quality of their product. However, little attention has been focused onto this subject at Smalltalk-centered conferences or in Smalltalk literature (see the workshop at OOPSLA-95 for a notable exception). There is a particular scarcity of literature on "how-to" test Smalltalk systems as well as tools to support such testing. For organizations just starting to use Smalltalk for major projects this is a very worrying situation.

Object oriented techniques do not (and cannot) guarantee correct programs. They may well help to produce a better system architecture and an object oriented language may promote a suitable coding style, but these features do not stop a programmer making mistakes. Although this should be self evident, for a long time there was a feeling that object oriented systems required less testing than systems constructed with traditional procedural languages, was prevalent (for example see the book describing the OMT method by Rumbaugh *et al*).

Where the testing of object oriented systems has been considered it is often the user interface of the system which has actually been tested in a principled or systematic manner. These tests usually concentrate on overall system functionality, usability issues (such as the ability of the user to use the system or the speed of response) and stress or exception testing. Stress or exception testing relate to attempting to break the operation of the system by inputting unacceptable data or crashing part of the system (e.g. an associated relational database system) to ensure that the system can recover from such catastrophic failures.

However, this chapter aims to show you that if anything, object oriented systems require more testing, not less testing than traditional programming languages. In the remainder of this chapter we shall consider what effects inheritance, encapsulation, polymorphism and dynamic typing have on testing as well as approaches to method and class (unit) testing, object integration and system testing.

### 22.2 Why is testing object oriented systems hard?

#### 22.2.1 An example

To illustrate the point consider the following Smalltalk. Let us assume that we have defined a method `returnSymbol` which returns a symbol based on the current state of an object. Let us also assume that we have defined another method `passesTheTest` which returns true or false based on the state of its object. We could then write the following method:

```myMethod: aCollection and: anObject
    | aVariable |
aVariable := anObject returnSymbol.
aCollection do: [:item | (item passesTheTest) ifTrue:
    [item perform: aVariable]].
```

In this case it is very difficult to determine statically what is going to happen in this method. This is because until the method is executed, we do not know now the content (let alone the type (class) of the objects) in `aCollection`. We cannot determine therefore which objects must respond to the `passesTheTest` message. Nor can we determine which objects will return true from this message and thus which objects will cause the `ifTrue` block to be evaluated. In this example, the situation is
made worse, because the symbol held in a *Variable* (which is the name of the message to send to the item) is determined by *an Object* which is an object passed into this method. We therefore do not know:

1. What the contents of *aCollection* will be

2. Which classes must respond to the message *passesTheTest*

3. Which instances are likely to result in the *ifTrue* block being evaluated (this is context dependent).

4. What the message to be sent within the *ifTrue* block will be (this is content dependent).

5. Which objects must respond to the message we don't yet know.

It could be argued that this is true, but irrelevant. However, the point is that a number of the above issues would be resolved by the compiler at compile time in other languages such as Ada or Pascal. These static compile time checks can identify what type of object will be held in the *aCollection*, if the type of object receiving the message will understand that message etc. Where as in Smalltalk, it is left up to the developer to ensure that no problems are likely to occur.

As you can see from this very simple (although slightly contrived) example traditional static test generation techniques, although of use in object oriented languages such as Smalltalk, do not provide the whole answer. In the example above, the problems are due to the dynamic typing of Smalltalk and the late binding and polymorphism of object oriented languages.

### 22.2.2 What makes it hard?

Part of the problem for object orientation is that space of possibilities is so much greater. For example, not only are we concerned about objects sending messages requesting that other objects run specified methods correctly, we are also concerned with which class of object will receive the message and which class of object sent the message as this will therefore have an effect on which method will be used. These issues are related to the polymorphic nature of object oriented languages and the use of abstraction and inheritance as a basic system construction tool.

In addition encapsulation leads to a fundamental problem of observability; as the only way to observe the state of an object is through its operations. This is fine for black box testing, but makes white box testing extremely difficult. This problem is compounded as traditional control flow analysis techniques are not directly applicable as there is no definite sequential order in which methods will be invoked. That is, another object may request that a method is executed at a point which cannot be statically determined (see example in introduction). Indeed, as the state of the object may affect what the method does, it may not even be possible to determine how the method will behave. Of course this does introduce the issue of "what is correct behavior", however we will leave the issue of "correctness" until a later section.

In traditional programming languages the basic unit of test is usually taken to be procedure or function. However, it should be seen from this brief discussion that, the object oriented equivalent i.e. the method, cannot be taken as the basic unit of test. It is affected by the state of the object, possibly by the state of the class (if the class has class variables), may interact in unforeseen ways with other methods and may rely on methods defined elsewhere in the class's superclass hierarchy.

Inheritance also affects testing and introduces the following question "after a change, to what extent should the code currently in the system be re-tested?". In a traditional programming language it is usually quite straight forward to identify what parts of the system code are affected by some change. In an object oriented system it is far less obvious.

The implication of the above is that in object oriented programs, such as those implemented in Smalltalk, the basic unit of test must be considered to be the class. That is not to say that care is not given to exercising individual methods within a class, merely that the individual methods should not be treated in isolation.

### 22.2.3 Why a lack of emphasis?

Part of the reason for the lack of Smalltalk testing literature or tools is probably due to the background of Smalltalk. In the past it was often used as a single (or at most a few) developer's language. It was often considered to be a personal development environment or a good prototyping tool. However few
people considered it suitable for constructing industrial strength systems in. The reasons for this have
already been considered in the introductory chapters of this book.

As t he types of systems being constructed were experimental, the tools available made code
debugging easy and could be done even when the implemented system was executing. This led many
people to design the overall system, roughly specify the components, imple ment them and test the
resulting system (not the components) using the user interface. If the system failed (at any time
including once development was complete) then a quick fix could sort out the problem. This approach
can work well for a single develope r-user, non critical, application where the majority of the system is
the user interface. However, for serious development this approach is inappropriate.

Smalltalk applications require testing just as any other software requires testing. Indeed the type o f
testing normally applied to traditional programming languages is also required by object oriented
systems. However, object orientation imposes unique requirements on the testing process. Why these
requirements exist and how they can be handled is the subject of the remainder of this chapter.

22.3 Inheritance

Inheritance is a fundamental feature of object oriented programming languages. However, it is both a
blessing and a curse. For example, many programmers believe that they can inherit many of the features
they require in their new classes from existing classes (and often they can). However, they also tend to
feel that it is not necessary to test those features they have inherited because they have been tested very
many times before (both by the system develo pers and by the many thousands of users). However, this
is misleading because in defining a new subclass they have changed the context within which the
methods will execute at least in the subclass. The problem is that each new subclass may not require any
re-testing and may very well function acceptably, but you are relying on a continuing hypothesis. Of
course this hypothesis may have held many times before, but there is no guarantee that it will hold this
time. Interestingly, the same problem also occurs with Ada generics. In this case each instantiation of
the generic package may work as intended but equally this time it may fail.

22.3.1 The effects of inheritance

There are a number of ways in which inheritance can affect the testin g required in a subclass. The
following list summarizes these and lists the type of testing required:

**Adding functionality**. For example, by adding new methods. In these situations, the tests should concentrate first on the new functionality, to ensure tha t the class works correctly (relative to its specification). If the new functionality calls on existing methods, then tests should be performed to ensure that these methods continue to function correctly. If any existing instance variables are modified by the new methods, or the methods they call, then the effects of these changes should be determined. This may result in re-testing the whole class.

**Over-riding methods.** In these situations, the newly defined methods must of course be tested. This should test the new definition to ensure that it handles the same range of values as the original and produces the same results. If this is not what was desired, then over -riding was not the correct approach.

Next all the methods which invoke the overridden operatio n must be re -tested. This can be
done by using the tools provided with the Smalltalk system to identify all the senders of the
messages associated with the changed methods. This will provide the initial test list. If the
behavior of inherited methods is ch anged in any way then this should be treated as adding
functionality and tested as above.

**Super Class references**. If the subclass changes anything used by the super class, for example by referencing a pool variable, then the super class must also be subje cted to incremental testing. Again, the process needs to identify the elements of the class which have been affected and to test those elements. In the case of a pool variable, it may also involve testing other classes which reference the pool variable.
### 22.3.2 Incremental testing

Incremental testing is one obvious answer, i.e. we don't want to re-test the whole class if we don't need to, only those elements which have been affected by the additions in the subclass need to be tested. Of course this is the key issue here: what determines whether or not we “need to” re-test a subclass. One answer is that it is the properties of the class which are affected by the subclass, including which instance variables may be altered and which methods may be directly or indirectly affected, which determine what should be re-tested. For example, a directly affected method may be one which is called by a (re)defined method in the subclass or which directly references an instance variable which is modified by (re)defined methods. Indirectly affected methods are much harder to identify. They are methods which may be affected due to a chain of interactions. For example, suppose that a method “a” references an altered instance variable and modifies another instance variable. This results in a second method operating in a different way generating a different result which is used by a third method. The third method is indirectly affected by the original change in the instance variable. In the worst case, every element of a class may be affected in such a way. In such cases, either the whole class must be re-tested or the developers must use their own intuition and experience to decide how much testing is required.

A subtlety which can often be missed is that although an inherited method has not been changed, a method which it calls may have been redefined in the subclass. Thus the inherited method is directly affected by the subclass and must be re-tested.

However, we want to be sure that any inherited functionality has not been adversely affected in an unanticipated way (note that this implies that we have some sort of specification against which we can compare the functionality of the class). We must therefore also consider some form of regression testing. This can be achieved by defining an appropriate set of regression tests on the class side of the root superclass. These tests can then be performed whenever the root class or one of its subclasses is changed in anyway. Ideally these tests should include their own evaluation so that a report can be generated (to the Transcript or to a file) stating the result of the tests. If appropriate, additional tests can be defined (in subclasses of the root superclass) to test the added functionality. If new tests are defined each time the functionality is extended or modified, then the tests on the class side also act as a repository of testing knowledge which can be exploited by future users of the classes without the need to understand their full functionality.

## 22.4 Abstract superclasses

Abstract superclasses bring together common features, which are to be used in many subclasses. However, from a testing point of view they are a difficult problem. The definition of an abstract superclass is a class which is never intended to be instantiated and which does not provide, in its own right, enough functionality to be useful. Given these two features, it is extremely important to test abstract superclasses thoroughly. However as they do not provide enough information to create an instance of the class, it is impossible to test the class directly. The tester should provide a subclass which acts as a test harness for the facilities defined in the abstract class (such a class can be easily removed from the Smalltalk image).

The test subclass might provide a wide range of facilities. For example, a method may be defined which sets the state of the instance such that a particular method can be called and then sends a message causing the method (inherited from the parent class) to be executed.

Note that testing the abstract superclass does not mean that exhaustive testing of the methods in the subclasses, which inherit from it, is not required. As each subclass has changed the context within which the method will be run, the same approach to testing as discussed in Section 3 must be performed.

## 22.5 Encapsulation

Encapsulation is a great bonus from the point of view of the user of an object - they do not need to know anything about the object's implementation, only what its published protocols are. This also means that the developer of the class knows that any potential users will have to come through the front door etc.
However, for the tester of the class it is both a benefit and a drawback. It is a benefit because the encapsulated object is clearly designated for unit testing. That is, it is clear what the boundaries of the class are, i.e. anything that is not in the class is in another class and thus does not come into the picture (from the point of view of unit testing). It is therefore possible to test the unit in isolation.

### 22.5.1 Black box testing

Encapsulation therefore promotes black box testing. Black box testing can be carried out in the normal way with the addition that the state of the object (and possibly the class) must be taken into account. Black box testing (also called specification based testing) is aimed at testing a program against its specification only. That is, the class should be tested regardless of the way in which it has been coded. It is usually accomplished, even in object oriented systems, by identifying a set of messages to send to the object and the parameters to use with these messages. The results of sending these messages are then compared to the original specification (assuming one example). The product of this comparison is then used to determine if the class has passed the test or not. In general, if some results are not as expected further testing would be used to determine the actual behavior of the class. A decision would then be taken to determine what action was required (i.e. modification of the source code or if appropriate, modification of the specification).

### 22.5.2 White box testing

Although encapsulation promotes black box testing, it can make white box testing much harder. White box testing (also known as program-based testing) is complementary to black box testing. It consists of examining the internal structure of a piece of code to select test data which will exercise that code thoroughly either because that piece of code is critical or to gain confidence in the code to eliminate it from suspicion (for example if you are attempting to track down the source of some undesired behavior).

A problem with encapsulation is that while it is entirely possible to view the source code and identify the tests to perform, it is not possible to access the instance variables of the object directly, nor is it usually possible to monitor the execution of the methods externally to the object.

Of course in most Smalltalk environments you could use the debugger and the inspector to examine the source code during execution (and indeed this is the intention of these tools). However, using such tools presents problems for both traceability and accountability. Thus it is not possible to trace the tests which have been performed during a project nor is it possible to record these tests and their results for later quality audits. Of course, you can require the developer to note what they are doing, but this is nowhere near as good as having the system do it for you automatically. This approach also fails to support repeatability of tests. That is, once you have completed your testing, it is not possible to re-run the tests and check the results following some change to the class.

### 22.5.3 Overcoming encapsulation during testing

In Smalltalk it is possible to work around the problems imposed by encapsulation in a number of ways:

#### 22.5.3.1 Use of the halt message

Smalltalk provides a halt message which can be sent to an object. This will cause the object to stop executing at that point and allow the user to enter the debugger. The debugger will then allow the user to view the method that is executing as well as the state of any instance and temporary variables.

This may at first seem intrusive, as the developer is modifying the source code of the method by adding the halt message. As has already been stated this is not desirable. However, in this case, it is possible to control the method's execution through the debugger. This means that the developer can ensure that the intended operation of the method remains unchanged before attempting to identify the behavior of the method under test situations. Thus this approach may not be as intrusive as would at first appear.

It is unfortunate that the debugger is so named, as it is not only useful for debugging systems, it is also a particularly useful tool for white box testing. This is because, once you have entered the debugger (e.g. by introducing a self halt message into the source code of a method) it is possible to
control the execution of the method in a number of ways. For example, a tester can change the state of an object or the input parameter values using the inspectors at the bottom of the debugger. They can control the execution of the method either by stepping through the method a line at a time, descending into methods called by this method or proceeding to run the whole method as though the debugger had never been entered. By default the tester controls the execution of the method from the point immediately after the halt message. However, it is also possible to re-run the whole method.

### 22.5.3.2 Subclassing the test class

A more object oriented approach is to define a test subclass for the class being tested (this approach has already been mentioned above). The tester can then define an appropriate set of test harness methods which exercise the class in the desired manner. As the test harness methods have the same ability to access the state of the object as the methods under test, it is possible to determine the object's state before and after a method is executed. The results of these tests can then be written to the Transcript and / or to a file. If the results are written to a file, the tests can be re-run at a later date and the new results compared with the previous results. This is cleaner than writing print statements within the source code of the methods to be tested, however it does not allow the internal operation of the methods to be monitored as closely.

To monitor the actual execution of the desired methods, it is possible to place a `self halt.` message just prior to any super message. This invokes the debugger and allows the developer to step down into the super method's code. For example:

```
age
    self halt.
super age.
```

Of course, in practice a combination of all of the above may be necessary to accomplish both black box and white box testing effectively.

## 22.6 Polymorphism

Polymorphism is the ability of objects to send the same message to instances of different classes which do not have a common superclass. It is made possible by the dynamic binding of messages to objects. That is, it is only at run time that it is possible to determine to which object the messages will be sent. From the point of view of testing, there are a number of problems associated with polymorphism. Each of these is discussed separately below.

### 22.6.1 Undecidability

Polymorphism allows the programmer to specify which message will be sent to an object, but not which object will receive that message. This will only be determined at run time. It is therefore not possible to provide any form of static check to see that the message will be "understood" by the object (other than checking that at least one class somewhere in the system will respond to that message). In addition, even when it is known that the object receiving the message will understand the message (e.g. all objects in Smalltalk understand `printString`), it is not possible to identify which version or implementation of the method will be executed. That is, the tester will know that the object will understand the message `printString`, however, which version of `printString` will be used will depend on the class of the receiver. It might be that defined in class `Object`, that defined in class `Collection` etc.

It is therefore impossible for the tester to know what would be an appropriate range of tests for a polymorphic message expression. They therefore have to make assumptions about the range of objects which are likely to be sent the message. In some cases this range may be very large indeed. In such situations it is necessary to produce one or more class hierarchies for different implementations of the method. These trees can then be used to identify those classes in which new definitions of the method are provided. The assumptions being used in the tests should, of course, be made explicit in the test report, so that they can be challenged if it is felt that they were inappropriate.
### 22.6.2 Extensibility of hierarchies

Another problem associated with polymorphism is presented when an operation, which possesses one or more parameters which are polymorphic, is being tested. That is, the operation can be called with a range of different types of object as a parameter. For example, a parameter may be assumed to be a collection, however it could be a set, a bag, an array, a list, a sorted collection or an ordered collection. Each of these have specific features which may or may not affect the operation of the method. They may also contain elements which are of any type, which in turn may or may not affect the method's behavior.

As testing a method consists of checking its effects when executed for various combinations of actual parameters, a test suite must ensure that all possible input types are covered. This of course implies that some form of specification exists which specifies the acceptable range of input types. However, it is impossible to plan a set of tests in which you check all possible parameter values for a method. This is because the range of different types of objects which could be presented to a method is huge in Smalltalk. Remember that the hierarchy of classes is very large (e.g. every class under `Object`) and is freely extensible. For example, I can define my own stack and queue collection classes. However, even if the method I am testing includes a test to see if it has been presented with an instance of a collection class, it could easily encounter a type of collection class for which it was never designed. Remember you (or anyone else) can extend the set of collection classes whenever you wish. If the method is part of a class which is to be provided as part of a set of "highly" reusable classes, either within a single project or for external use, this is a significant issue.

### 22.6.3 Heterogeneous containers

As has been briefly mentioned above, there are many classes which are designed to hold elements which may be any instance in the system. In Smalltalk, this means that they can hold instances of classes, classes, metaclasses etc. If the tester is considering a method which will be applied to, or will consume, the members of such a heterogeneous container (e.g. a collection), it is almost impossible to ensure that a complete range of tests has been performed. For example, it may be assumed that all the members of such a container are conventional instances of user defined classes, however there is nothing to stop the member of one of these containers being a class or the system dictionary etc.

In such situations care must be taken to ensure that reasonable usage of the method has been tested. In other words, with reference to the classes specification, assumptions must again be made, this time about the use of the method. Again, these assumptions should be documented so that they can be challenged if necessary at a later date.

## 22.7 Additional Smalltalk specific features

### 22.7.1 Block evaluations

Blocks introduce another complexity to the testing process which is particular to Smalltalk. Not only are they more difficult to test physically (it is more difficult to get hold of them in isolation), they also execute in the context within which they were created (rather than the context in which they are evaluated). This second issue is important because in many situations a block is defined in one place, and then passed as a parameter to another place, where it is evaluated. If the receiving method is being tested, it is impossible to conceive of all the contexts in which the block could be created and therefore impossible to produce a comprehensive test suite. Again, care must be taken to test the receiving method against its specification and not the assumptions which this led to.

In addition when they are examined in the debugger, they are treated in a different way to general Smalltalk code, which makes it more difficult to follow the thread of execution. Instead of immediately stepping through the code in the block, it is necessary to step down into the value method and then into the block. It is all to easy to step over the block and fail to see the result.
### 22.7.2 The Perform: message

The perform message takes a symbol as its parameter. It then uses this symbol as the name of a method to execute on the receiving object. As the symbol can be stored in a variable, it is impossible to determine all the possible symbol values which will be passed to the perform message. It is therefore advisable not to use perform unless it is absolutely necessary. However, if the developer has used a perform expression, then a reasonable set of tests should be identified. Once again, the assumptions used to generate this set should be made explicit.

## 22.8 Summary

In this chapter we have considered some of the special problems which face a developer when testing an object oriented system (with special consideration for Smalltalk implementations). As has been discussed inheritance, abstraction, polymorphism and encapsulation all play a major part in determining the best practice in testing Smalltalk systems. A number of recurring themes have been:

* The importance of specifying what a class or object is intended to do.

* The use of scenarios to aid in the adequate testing of methods and classes.

* The adequacy of testing and the importance of deciding what is sufficient.

* Examples and assumptions need explanation (including their context).

A final comment is that this chapter should not put you off constructing large complex systems in Smalltalk. Rather it should make you aware of the difficulties you will face in testing such a system.

## 22.9 Further reading

Little has been written about the special problems of testing object oriented systems, however the papers by [Barbey and Strohmeier 1994] and [Barbey, Amman and Strohmeier 1994] are an exception and provide an excellent introduction to the subject of testing object oriented systems. [Perry and Kaiser 1990] a discussion of the effects of inheritance on object oriented testing. The next chapter also provides further reading on the subject of object oriented testing.
# 23. Method and Class Testing

## 23.1 Introduction

In the last chapter we discussed the problems facing the tester of an object oriented system (and in particular a Smalltalk system). We also briefly considered some approaches which overcome these problems. In this chapter we will consider the current best practice in testing object oriented systems.

### 23.1.1 Class and instance sides

As was stated in the last chapter, the basic unit of test in Smalltalk is the class. However, there are two sides to a class, one is referred to as the *class side* and the other the *instance side*. The class side can be tested directly by sending messages to the class. However, the instance side can only be tested by creating instances of the class. That is, although you define the instances' methods in the class, you must test them using an instance. An important point to remember is that, it may be necessary to use both the class and instances of the class together, to adequately test both the class side and the instance side. For example, let us assume we have a class such as that illustrated in Figure 23.1.

<figure><img src="image_96.png" /><figcaption>Figure 23.1: Instance and Class method interaction</figcaption></figure>

In this situation, it is possible to test the class side method `currentLinkType` directly by sending a message to the class and in isolation. However, it is not possible to test the instance method `createLink` without considering the range of values which may be returned by `currentLinkType`. In this case, it is the class `XLinkType` which is returned, however, it could easily be one of a range of values. In addition, the class to which the `currentLinkType` message is sent, depends on the class of the object in which the `createLink` method is executing. As the `createLink` method may be inherited by other classes, we cannot guarantee which class will receive the `currentLinkType` message.

### 23.1.2 Testing methods in a class

Each method in a class should first be tested in isolation. However, once all the individual methods in the class have been tested, threads through the methods in the class should be identified. This can be done by postulating scenarios of normal and exceptional usage (which may have been produced when the class was being designed). By tracing the result of these scenarios through the class it is possible to identify threads of execution amongst the methods in the class. An example of a thread of execution is illustrated in Figure 23.2. This thread was obtained by considering a scenario in which a `person` object has a birthday. This leads to the `person` object being sent the message `birthday`. The associated method `birthday` is presented below:

**birthday**

Transcript show: 'A happy birthday to ' , self name.
self incrementAge.
Transcript show: 'I am now ' , self age.
From the scenario that a person might have a birthday and therefore receive the message birthday, and from examining the source code of `birthday` (and `incrementAge`) we can obtain the thread in Figure 23.2.

<figure><img src="image_97.png" /><figcaption>Figure 23.2: A thread of execution through an object</figcaption></figure>

### 23.1.3 Object state

Another point to bear in mind when testing a class, is that both instances and classes have state. For example, a class may have a class instance variable which is used to keep a record of the number of instances which have been created of that class. This information may be used purely for book keeping or may be used to limit the number of instances created. In the latter case, a test needs to be performed which sets the counter to its limit and then a new instance should be requested. This could be done, either by creating the required number of instances, or by manually setting the counter to the appropriate value (e.g. by using the debugger or inspector).

It is also important to remember that although a method has been tested in the context of a state instance, it is possible that some unintended sequence of message sends could result in the method being executed with the object in an unintended state. It is therefore not possible to test individual methods in isolation thoroughly, nor is it sensible to ignore the state of the object when performing these tests. It is of course quite possible to ignore the state, however the tester does so at their peril.

### 23.1.4 Private methods

Some methods are defined such that the user of the class is never expected to see them. This may be because they do some housekeeping which is local to the object (for example the initialization method) or because they provide facilities which are relevant to the internals of the object and not to the external interface to the object. In Smalltalk, this method hiding is not enforced, it is usually left implicit (unlike C++ or Java in which the equivalent of private methods really are private and are not available as external messages). For example, in VisualWorks Smalltalk this can be done by placing the appropriate set of methods in a protocol entitled `private` or `private-*`. The convention is that a user of this class is not expected to use or to need to know about these methods. This is useful from a testing point of view as the tester can test the individual execution of these methods in a black box fashion. They can also use the approaches described above to perform white box testing. However, it also means that a user of the class, if they so wish, can call these methods. As this is not what is intended it is arguably reasonable to ignore this scenario. However, it may be wise in some situations at least to know what the effects will be and whether the object's methods will respond in a meaningful or at least well defined manner. For example, it may be useful to know what will happen if another object sends the message `initialize` to the current object at some point after the object has started to be used.

### 23.1.5 Pool variables

An additional problem with testing classes (as opposed to unit testing in other languages) is the presence of pool variables. These are rather like limited scope global variables. They are common to a set of classes and may affect the operation of these classes. As any one of the classes associated with the pool can change their value it is possible for one class (or its instances) to have a profound effect on the operation of a completely different class or its instances elsewhere in the system. This is something which requires careful thought.
### 23.1.6 Tracing a class's operation

In order to trace a class's operation it would be possible to create a subclass which mirrored the parent classes methods exactly. The subclasses methods could print the value of any parameters into the method before calling the method and then any results returned by the method. If necessary tests could be performed to ensure that the parameters or the returned value were within anticipated ranges. The advantage of testing the class method calls in this way would be to encapsulate all the test information into a test subclass which could be removed from the Smalltalk image for the delivered system.

### 23.1.7 Sources of test information

The concept of use cases discussed earlier in this book is an important source of test cases. Opportunities for the merging of the use case specifications into test specifications abound.

A style issue, rather than a testing issue, is that it is good style to provide a set of test examples, often on the class side, which exercise some behavior of the objects of the class. These can be used to illustrate how to use the objects of the class. They can also act as a form of regression testing either when the class is modified or when a subclass is defined. If these test methods are placed in appropriate protocols, then the whole protocol can be filed out in one go, thus creating a test file.

## 23.2 Object integration testing

The above section deliberately only deals with the issue of testing a class in isolation. This involves individual method testing followed by testing combinations of methods. Object integration testing involves testing instances of the same or different classes when they are collaborating in some operation. This is different from traditional procedural integration testing as the structure of traditional programming languages is fairly rigid and is unlikely to vary while the system is executing. It is also different from statically bound procedural programs, because when a language such as Pascal is used, the compiler is able to test to see whether the called procedure or function definition matches the calling definition. For example, if a procedure expects an integer as a parameter the compiler checks that an integer is being presented to it.

In Smalltalk, neither the structure of the objects is fixed nor are the actual types of parameters etc. likely to be known. It is therefore necessary to perform object integration testing in a methodological and principled manner. If the specification of two classes indicates that they are intended to collaborate to achieve some behavior, then those two classes should be tested together. Again the specification should be used to generate scenarios which exercise normal and abnormal interactions. These scenarios can then be used to define tests to perform. Having tested the two classes other cooperations may be identified involving these and other classes. Each of these collaborations also needs to be tested.

Integration testing (like multiple method testing in a single class) relies on the identification of scenarios which will be used to define appropriate test suites. However, consideration should also be given to identifying anticipated message paths. The differences between these two tests are akin to black box and white box testing for a single method. The scenario based testing relies on testing the specification of the behavior of the collaborating objects, while the white box testing is intended to test particular message paths through the classes. Again, the two approaches to testing are intended to be used together.

## 23.3 System testing

System testing of Smalltalk systems should be carried out in essentially the same way as system testing of any computer system. This is because a system implemented in Smalltalk should be subject to the same types of requirements as a system written in any other language. There are some special situations which you might wish to test for, such as ensuring that the system does not run out of memory before a garbage collection is forced or that the system will still work once the image has been “stripped” of those classes not required by the production system (e.g. the compiler classes etc.). However, in general the system should be subject to the same range of tests as any other.
## 23.4 Self testing

An approach, suggested by Charles Weir of Object Designers Ltd., would be to provide facilities which enable code self-testing within Smalltalk. A simple technique is to make the code do its own self-testing using assertions. This is a code statement that is always true; it tests that the code is behaving as expected. If the statement is false the assertion stops processing to allow debugging.

An implementation of assertions in Smalltalk is

assert

self value ifFalse: [self halt].

This method should be added to the BlockContext class and allows the developer to verify assertions wherever required, for example:

[range first == 1] assert.

Of course in the released version of the system, these tests are redundant and waste processing time. So they could be replaced by a null version of the method:

assert

self.

The overhead of the extra method dispatch is relatively small; of course if it is still significant, we could write a utility to find the senders of the method and to comment out the assertion statements.

Assertions are particularly useful for testing preconditions, postconditions and class invariants. The Syntropy design method makes extensive use of such assertions [Cook and Daniels 1994 ]. It would therefore be possible to use the assertions identified during the design as the basis of the assertions to place in the Smalltalk code.

This approach could be taken further with the introduction of universal quantifiers (there exists and for all). This would allow expression such as if there exists a value x. The introduction of such quantifiers is not without its problems but should be possible. In addition assertions could also be used to ensure that the relationships between pre and post values during some operation is appropriate. Again this would not necessarily be straight forward, but it should be possible.

## 23.5 Summary

Throughout the chapter, guidance has been given to ways to overcome the problems inherent in testing object oriented systems. In the next chapter we shall consider a simple set of extensions to the basic VisualWorks environment which greatly simplify the whole testing process.

## 23.6 Further reading

As there is so little written about testing object oriented systems, this section provides an extended set of references on the subject.

In 1994 a special issue of the Communications of the ACM was published which focused on testing object oriented systems [Binder 1994]. In this special issue a number of papers cover topics related to object oriented testing. For example, [Binder 1994b] discusses design for testability in object oriented systems which is a particularly important issue given the effects of inheritance, polymorphism and particularly encapsulation on testing.

Other papers relevant to object oriented testing include [Beck 1994], [Hoffman and Strooper 1995], [Hunt 1995], [Jorgensen and Erickson 1994], [Siepmann and Newton 1994].

The workshops on testing held at the OOPSLA conferences are also a useful source of references. For example the workshop held as the OOPSLA '95 Conference. For more information contact Barbara Yates, OOPSLA Workshop, 2002 Parkside Court, West Linn, Oregon, 97068 -2767, USA (Email: barbarabytesmiths@acm.org).
A good discussion of incremental testing for C++ can be found in [Harrold, McGregor and Fitzpatrick 1992]. While [Feigenbaum 1995] discusses some of the features which could be introduced into Smalltalk which would improve its potential as a development language.

The Syntropy object oriented development method makes explicit use of assertions. This method is documented in [Cook and Daniels 1994].

There are also many references which discuss various aspects of testing which are as appropriate for object oriented systems as they are for procedural systems. For example, [Beizer 1990], [Freedman 1991], [Myers 1979], [Pyle 1991] and [ISO 1993].
# 24. Smalltalk Style Guidelines

## 24.1 Introduction

Good programming style in any language helps promote the readability, understandability and clarity of your code. In many languages there are established standards to which many people adhere (sometimes without realizing it). For example, the way in which a C or Pascal program is indented is such a standard. However, style guidelines which have evolved for these procedural languages do not cover many of the issues which are important in Smalltalk. These are issues relating to classes, instances and methods. As non-object oriented languages do not have these concepts, they do not have standards for dealing with them.

Of course you should not forget all the pearls of wisdom and programming practices that you have learnt using other languages. Just as in any other language there are a number of acknowledged bad practices (which are not specific to Smalltalk), for example, the use of global variables! In this chapter (as in the whole of the book) we will assume therefore that you do not need to have the basic concepts explained, instead we will try to concentrate on those style issues which are specific to Smalltalk.

The remainder of the chapter is structured in the following manner: Section two considers style issues associated with variables including temporary variables, instance variables as well as class, class instance and pool variables. Section three provides some guidance on the use of classes within Smalltalk. Section four considers methods, their naming, use and code placement within methods. Section five discusses how to name message protocols for both instance and class methods.

## 24.2 Variables

### 24.2.1 Naming variables

In Smalltalk variable names such as `t1`, `i`, `j`, or `temp` should rarely be used. Instead variable names should either be descriptive (known as semantic variables) or should indicate the type of object which the variable will hold (known as typed variables). Which approach should be chosen depends on both personal style and the situation in which the variables are being used. Instance and class variables, for example, tend to have semantic-oriented names, while parameters to methods often have typed names.

The semantic approach has the advantage that less needs to be assumed about what the variable will be used for. Since subclasses can inherit instance and class variables, the point at which they are defined and the point at which they are used may be very remote. Thus any contextual meaning and commentary, provided with their definition, will have been lost. Examples, of semantic variable names include:

score
currentWorkingMemory
TotalPopulationSize

The typed approach is often adopted for parameter names as it is easier to see what type of object is required as an argument to a method. For example:

add: anObject ifAbsent: aBlock
at: anIndex put: anObject

Although some methods mix the use of the two, for example: `at: key put: anObject`.
Temporary variables, which are local to a method, often have a mixture of semantic and typed names. Larger methods often have semantic local variable names due to the additional complexity they represent.

<table><caption>Table 24.1: Variable naming conventions</caption><thead><tr><td>Variable type</td><td>Convention</td><td>Variable type</td><td>Convention</td></tr></thead><tbody><tr><td>Global variable</td><td>Capitalized</td><td>Temporary variables</td><td>Lower case</td></tr><tr><td>Class variable</td><td>Capitalized</td><td>Class instance variables</td><td>Lower case</td></tr><tr><td>Class names</td><td>Capitalized</td><td>Instance variables</td><td>Lower case</td></tr><tr><td>Pool variables</td><td>Capitalized</td><td>Method parameters</td><td>Lower case</td></tr></tbody></table>

In addition, if the variable name is made up of more than one word, the words should not be separated by ‘-’ or ‘_’, but by the capitalization of the first letter of all but the first word. For example:

theDateToday
employeeRecord
objectInList

Whether the first word in the variable is capitalized or not depends on the type of variable being used. Table 24.1 summarizes the conventions.

### 24.2.2 The role of variables

Instance variables, class variables and class instance variables all play particular roles within a Smalltalk system:

**Instance variables** should only be used for holding local data or for holding references to other objects.

In the latter case, these other objects should be involved in some form of collaboration with the object (otherwise why does the object have a reference to them).

**Class variables** should only be used as ‘constant’ values which are referenced by all instances of a class. They should never be used as a form of limited global variable. Such a use is frequently an indication that a solution has not been designed with the proper amount of care and attention. A very nice use of class variable can be found in the database classes of VisualWorks.

**Class instance variables** are often ignored by most Smalltalk programmers because they are not sure what to do with them. This is partly due to the lack of their use in much of the VisualWorks image and partly due to the rarity of occasions when their use is justified. A class instance variable, is a local variable for the class. They are only accessible by the class itself and each class will hold its own copy of the data.

Class instance variables should only be used when either:

* classes need to hold some local data which instances should not have access to (otherwise use a class variable)

* each class in a particular class hierarchy needs to hold its own copy of some data which should not be shared with other classes.

If you wish to see an example of their use look up `UILookPolicy` in the VisualWorks image.

### 24.2.3 The role of pool variables

Pool variables are global variables whose scope is limited to those classes which have (explicitly) joined the pool. They should not be used as a cheap way of sharing information amongst a disparate set of classes (for example as a means of providing a global variable without the stigma of a truly global variable). In general their use is really on the justifiable within an application framework (a set of cooperating objects of different classes). Such frameworks often require some common shared values. For example, the text style to use in an application, the name of a preference file or the settings obtained from such a file. Pool variables can then be used to record this information once, but allow it to be used by all the objects within the framework.
### 24.2.4 Adding variables to system classes

Always add class and instance variables to the end of any existing variables in a system class. This is because the interpreter on some Smalltalk implementations accesses the variables of certain classes by *position* rather than by name!

### 24.2.5 Accessing instance and class variables

In general it is always better to access in stance and class variables via intermediate methods, referred to as *accessor* methods, rather than access or set them directly. This is called *variable-free programming*. This promotes the modularity of your methods and insulates the methods against changes in the way the object (or class) holds instance (or class) information. This is a very important concept, as direct access to instance variables can limit the power of subclassing.

It is also possible to protect the instance (or class) variables from undesired changes. For example, you can put preconditions on an access method, or return a copy of the contents of the variable so that it cannot be directly affected.

## 24.3 Classes

### 24.3.1 Naming classes

The naming of a class is extremely important. The class is the core element in any object oriented program. Class names are always capitalized. For example:

* Collection

* GraphicalClassBrowser

* EmployeePensionPaymentsHistory

The second and third class names above provide extremely good examples of how a class name can describe the class. This is because the name of the class is used by most developers to indicate its purpose or intent. This is partly due to the fact that it is the class name which will be used when searching for appropriate classes (for example by using the *find class...* menu option).

The class name should therefore be descriptive, for example, classes with names such as MyClass or ProjectClass1 are of little use. However, class names should not be so specific that they make it appear that the class is unlikely to be of use except in one specific situation (unless of course this is the case). For example, in an application to record details about university lecturers, a class with a name such as ComputerScienceDepartmentLecturer is probably not appropriate unless it really does relate only to lecturers in the computer science department. If this is the case, you need to ask yourself in what way are computer science lecturers special over other lecturers.

### 24.3.2 The role of subclasses

A subclass should only be used to modify the behaviour of its parent class. This modification should be a refinement of the class; this can be accomplished in three ways (or in combination):

* Changes to the external protocol, that is the set of messages that the instances of the class respond to.

* Changes in the implementation of the methods, that is changes in the way the messages are handled.

* Additional behaviour, which references inherited behaviour.

If a subclass does not provide one or more of the above, then it is incorrectly placed. For example, if a subclass implements a set of new methods, but no reference is made to the parent classes' instance variables or methods, then this class is not really a subclass of the parent (it doesn't extend it).
The one exception to this rule are subclasses of `Object`. This is because `Object` is the root class of all classes in Smalltalk and as you must create a new class by subclassing it from an existing class, it is typical to subclass off `Object` when there is no existing appropriate class.

### 24.3.3 Capabilities of a subclass/class

A subclass or class should accomplish one specific purpose, that is it should capture only one idea. If more than one idea is encapsulated in a class you may be reducing the chances for reuse as well as contravening the laws of encapsulation in object oriented systems. For example, you may have merged two concepts together so that one can directly access the data of another. This is rarely desirable.

This guideline leads to small classes (in terms of methods, instance variables and code). Breaking a class down costs little but may produce major gains in reusability and flexibility. If you find that when you try and separate one class into two or more classes, some of the code needs to be duplicated for each class, then the use of abstract classes can also be very helpful. By placing the common code into a common abstract superclass, you may be able to avoid unnecessary duplication of that code.

The following two guidelines are intended as an aid in identifying when a class should be split:

1. Look in the class comment (if there is no class comment than this is a bad sign in itself). Consider the following points:

* Is the comment short and clear. If not is this a reflection on the class? If it is, then consider how the comment can be broken down into a series of short clear comments. Now base the new classes around these comments.

* If the comment is short and clear, do the class and instance variables make sense within the context of the comment? If they do not, then the class needs to be re-evaluated. It may be that the comment is inappropriate, or the class and instance variables are inappropriate.

* If the comment is short and clear, do the class and instance methods make sense within the context of the comment? Again if they do not then appropriate action should be taken.

2. Look at the instance variable references (i.e. look at where the instance variable access methods are used). How are the instance variables being used? Is their use in line with the class comment? Again, if the answer to this is no, then appropriate action should be taken.

### 24.3.4 Changes to system classes

In general attempts should be made to avoid making changes to system classes if at all possible. Such changes can produce surprising side effects as well as reduce the portability of your code and others. In addition you may find it difficult to support the changes you have made to the system classes in future releases of the system.

However, there are certain circumstances in which it is necessary and useful to extend system classes. However, it is an extremely bad idea to modify an existing method of a system class. This is because if you are not absolutely sure about what you are doing you can affect the behaviour of very many objects. If you really feel you have to modify an existing class's method(s), then it is much better to subclass off the existing system class and modify the method(s) behaviour(s) there. Then if you do something which has unanticipated effects, those effects are localized.

If you are merely adding new methods to an existing system class, then it is a very good idea to place your additions in a separate category. This means that not only is it easier to find these additions, but that you can file out the category to a different file so that when updates to the system are delivered, you can easily patch in your extensions.

### 24.3.5 Creating new collection classes

When working with collections, there is always the question of whether to subclass a new collection class to hold your data (as we did for the `Queue` and `Stack` classes) or whether to define a non collection class which will hold a collection within one of its instance variables and then provide methods which will access that instance variable. For example, let us assume that we wish to define a new class called `Account`, which will hold information on deposits and withdrawals. We believe that we
should use a dictionary to hold the actual data, but should **Account** be a subclass of **Dictionary** or
a subclass of something else (for example **Object**)? The two options are illustrated below:

Object

Account has in an instance variable holding an instance of class *Dictionary*

or would it be better to have:

Object

Dictionary

Account

Of course this point also depends on what you are going to do with the Account class. If it is
providing a new collection class (in some way) even if it is only for your application, then the above
should be born in mind. However, if what you are actually doing, is providing a functionally complex
class which just happens to contain a dictionary, then the above would be the wrong way to do it. In this
case it is almost certainly better to make the new class a subclass of Object.

There are two further points to bear in mind about this. The first is that if you define any instance
variables in Account, then when you examine any instance of Account you will find that you get the
Dictionary inspector which does not display the instance variables. This can be quite infuriating. Of
course you can subclass the inspector and extend it so that it does display the instance variables. A
second point is that if an instance of Account needs to grow, then the contents of the instance
variables defined in Account will apparently disappear, unless you extend the copyEmpty method it
will have inherited. This method must be extended such that the contents of the instance variables is
copied. Again, this is only a minor point, but can be extremely annoying when you have spent a great
deal of time attempting to track down a bizarre bug, only to find this is the root cause of your problem.

24.3.6 Class comments

Every class should have a class comment, no matter whether it is an abstract or concrete class. This
comment should act as the basic documentation for the class. It should therefore tell both a developer
subclassing from the class, or a user of the class, what they need to know. The comment may also
contain information about the author's name, affiliation, history of the class's modifications, as well as
its purpose and status. Information which might be placed in the class comment includes the name of
the class author, its purpose, who has modified the class, when and why, the type of class, instance and
class variables (including the class of object they will hold and their use), collaborations between this
class and others, example usage, copyright information and class specific information such as what a
subclass of an abstract class is expected to redefine.

24.3.7 A class or an instance

In some situations it may only ever be nec essary to create a single instance of a class. This single
instance must be created and then referenced where it is required. A point of continued debate is
whether it is worth creating such an instance or whether it is better to define the required behavi our in
class methods and reference the class (after all a class is an object as well). Invariably the answer to this
is no, for the following reasons:

* Such an approach breaks the standards which have been set, not only in the VisualWorks image, but by the majority of Smalltalk developers. You will therefore be reducing the readability and comprehensibility of your system.

* The creation of an instance requires a minimum of overhead. After all this is a key feature in Smalltalk and it has therefore received extensive attention.

* You may require more than one instance sometime in the future. If you have implemented all the code on the class side, it will be necessary to move these methods onto the instance side of the class.

* In doing so, you may be tempted to tr eat the class as a global reference. This suggests that the implementation has been poorly thought out.
## 24.4 Methods

### 24.4.1 Naming methods

Method names should always start with a lower case letter. If the method name is made up of more than one element then each element following the first one, should start with a capital letter. This standard follows that used for variable names. For example:

```
account deposit: 100
account printStatement.
```

If the method is a keyword method containing more than one parameter, then each of the key words should start with a lower case letter, with each subsequent word starting with a capital letter. For example:

```
Dialog request: 'Name' initialAnswer: 'John'
onCancel: [Transcript show: 'Error'].
```

The naming of methods is extremely important. An appropriate method name will not only make the code easier to read it will also aid in reuse. Therefore method names should be selected to illustrate the method's purpose. In addition, it is common to try to select a name which makes it possible to read an expression containing the method name in a similar manner to reading a sentence. For example:

```
statement deposit: 100 on: Date today.
```

This is actually helped by the ability to spread the arguments amongst the method name.

Methods which return *true* and *false* as the result of some test follow a common format throughout the Smalltalk system. These methods use a verb such as *is* or *has* concatenated with whatever is being tested, for example :

```
isString
isActive
hasFood
```

Notice that in the first case the method is testing to see if the receiver is an object of class *String*. It therefore uses the name of the class in the method name. In the next two cases some aspect of the receiver is being tested. For example, the second method may test to see if a *process* is active or not. In this case, the value being tested for is used as part of the method name.

### 24.4.2 General comments about methods

In general it is better to percolate a method up as high as possible in the inheritance hierarchy as long as it makes sense. The higher the method is, the more visibility it has to classes in other branches of the hierarchy, and the more method level reuse you can achieve. You should also consider:

**The role of a method.** Think carefully about the purpose and placement of methods within a class. Just as a class should have a specific purpose, at a lower level a method should also have a single purpose. If a method is used to perform more than one function, then it should be divided into separate methods. In general terms methods should be no longer than one page of A4. Small but beautiful methods are desirable.

**Code placement within methods.** Deciding how to break up the desired functionality into procedural elements can be difficult in procedural programming languages. In Smalltalk it is made more difficult by considerations of object encapsulation and reuse. However, there are a number of questions which you can bear in mind when determining whether your code is correctly placed within the methods you have defined:

* If a method does not refer to any aspect of the object (e.g. super, self, or instance variables etc.) what is it doing? Should the method be there?
* A method should only send messages to a limited set of objects. This promotes maintainability and comprehensibility and increases the modularity of the method.

* Have you used accessor methods for instance variable access? This is known as *variable-free programming*. This can greatly insulate the method from changes in how the object holds information.

* Is the behaviour encapsulated by the method intended for public or private (to the object) use? If it is a mixture of the two, then the method should be decomposed into two or more methods. The private code should be placed in a method which is defined within a private protocol (see below). This indicates to developers that the method is not intended for external use.

* Does the method rely more on the behaviour of other objects than on its own object (that is, a series of messages is being sent to some object(s) other than "self")? If so, the method may be better placed in another object (or objects).

This last point is worth considering in slightly more detail. The series of messages in such a method may be better placed in a method in the class of the receive r object. This is because it is really describing behaviour associated with that object. By placing it with the receiver object's class, all modifications to the behaviour of the receiver are encapsulated in that object. In addition, this behaviour may be useful to other objects, by encoding it within the receiver's class, they can all gain access to that behaviour (rather than having to duplicate it in a number of places). It is not easy to get good method level code reuse with poorly placed code. To do this most messages should be sent to self. Note: This is probably one of the hardest things to do well in object oriented programming. However, if done correctly it can pay very high dividends.

### 24.4.3 Class methods versus instance methods

It may at first seem unclear what should normally go in a class (side) method as opposed to what should go in an instance (side) method when defining a new class. After all they are both defined in the class! However, it is important to remember that one defines the behaviour of the instance and the other the behaviour of the class (the class side methods). The class side methods should only perform one of the following roles:

**Instance creation.** This role is very important as it is often used to initialize instance variables of an object to appropriate values. For example, it is quite common to see a class side method called new used to send the message initialize to a new instance.

**Class variable access.** See below for a discussion of this role.

**Inquiries about the class.** Such methods answer with generally useful objects, frequently derived from class variables. For example, they may return the number of instances of this object created.

**Instance management.** To control the number of instances created. For example, some classes only allow one single instance of that class to be created.

**A documentation role.** Class methods solely for documentation are sometimes used. Generally the class *comment* is a better place for this information unless such a facility is not available e.g. in Smalltalk V.

**Examples.** Occasionally class methods are used to provide helpful examples which are aimed at explaining the operation of a class. This is good practice.

**Testing.** Class side methods can be used to support testing of an instance of a class. Such methods can be used to create an instance, perform some operation and then compare the result with a known value. If the values are different an error can be reported. This is a very useful way of providing regression tests.

**Support for one of the above.**

Anything else should be performed by an instance method.

### 24.4.4 Class instance creation methods

It is quite common to want to redefine the way in which a new instance is created. This can be because you wish to:
* provide a more meaningful instance creation interface (for example, Line from: (3 @ 3) to: (5 @ 5))

* ensure that certain information is provided which is necessary to instantiate the class (for example Account newBalance: 24.00)

* force an initialization routine to be executed before any user of the object can send it any other messages.

We shall consider the correct way to achieve the last of the above points. That is, we wish to force the execution of an initialization method before returning the newly created instance. To do this we can redefine the message new in the instance creation class side protocol of a class as follows:

new

^super new initialize

This method says, use the inherited method new to create a new instance of the class and immediately send that new instance the instance message `initialize` (this is extremely good Smalltalk style). In turn, the initialize method (defined on the class side) should have a format which follows this pattern:

initialize

super initialize "to initialize inherited instance variables"

"initialize variables that I define"

### 24.4.5 Programming in terms of objects

It is all too easy when you are first starting with Smalltalk to write procedure oriented code within methods. Indeed, in version 1.0 of VisualWorks there was code which had clearly been written by a C or C++ programmer rather than a Smalltalk developer. In such situations the developer has been thinking in too procedurally a manner. In Smalltalk what the programmer should try to do is to think in terms of objects.

### 24.4.6 Example methods

Example methods are class side methods which illustrate how the instances of an object should be used. They are particularly useful for complex classes which form part of a framework. In these classes it is often difficult to comprehend how the various classes work together, with `initialize` out seeing them work together. The example methods can then be filed out in a production system as they are not required for the correct functioning of the class.

### 24.4.7 Test methods

These have already been mentioned above and were discussed at length in the last few chapters. However, they are an extremely useful tool for the developer and should be encouraged at every opportunity.

## 24.5 Naming message protocols

An important style consideration for any VisualWorks developer is the naming of protocols (or message categories). It is a good idea to follow the conventions laid down in the existing classes for two reasons:

1. your own classes will follow the same pattern as those which have already been encountered and will thus be easier to follow for you and others

2. tools such as the Full Browser will allow you to see methods, defined in other classes higher up the class hierarchy, in a given category. When you want to name a new category you can search for similar methods, when you find them you can merely use the same category name.
<table><caption>Table 24.2: Class message categories</caption><thead><tr><td>Protocol</td><td>Use</td></tr></thead><tbody><tr><td>instance creation</td><td>Used for methods associated with creating instances</td></tr><tr><td>class initialization</td><td>Methods which initialize class variables</td></tr><tr><td>examples</td><td>Methods which illustrate some use of the class, e.g. instance creation</td></tr><tr><td>private</td><td>Methods that provide support for one of the above</td></tr></tbody></table>

Table 24.2 illustrates the common class message protocols and what they are used for. Table 24.3 does the same thing for the instance message protocols. Notice the practice of naming instance message protocols as actions. These actions are intended to indicate the type of activity being performed. A n exception to this is the initialize (or its variant initialize -release) protocol. Although this does not conform to the standard it is consistent throughout the VisualWorks image and you are therefore advised to comply with this exception.

<table><caption>Table 24.3: Instance message categories</caption><thead><tr><td>Category</td><td>Use</td></tr></thead><tbody><tr><td>accessing</td><td>Methods used for accessing and updating instance variables.</td></tr><tr><td>adding</td><td>Used in collection classes for methods which add elements to the collection.</td></tr><tr><td>converting</td><td>Holds methods which convert one instance into another instance.</td></tr><tr><td>comparing</td><td>Groups methods used for comparison tests.</td></tr><tr><td>displaying</td><td>Holds methods which relate to displaying information graphically</td></tr><tr><td>initialize</td><td>Holds methods which initialize the instance.</td></tr><tr><td>printing</td><td>Methods associated with printing the instance such as printString</td></tr><tr><td>updating</td><td>Used to hold methods which cause some change in the instances state.</td></tr></tbody></table>

## 24.6 Further reading

There are a number of places which can provide very useful further reading, for example [Skublics *et al* 1996] is an excellent little book which really does provide pearls of wisdom on good Smalltalk style. It is a book to be dipped into, rather than read from cover to cover. However, if you do manage to inwardly digest this book, you will be able to write clear, concise, reusable Smalltalk. In addition many of the core system classes also provide excellent examples of the good Smalltalk style.
Part Five

# Graphical interfaces in Smalltalk
# 25. The Perform and Dependency Mechanisms

## 25.1 Introduction

This chapter introduces the use of the perform mechanism which provides an alternative method for sending messages. It then presents a practical discussion of the dependency mechanism. This includes what the dependency mechanism is, why you might want to use it, how to construct a dependency and the effect that it has.

## 25.2 The Perform mechanism

The perform mechanism is a very important feature of Smalltalk. Some people consider it to be the most powerful feature available to the developer while others consider it essential for the construction of truly reusable classes (it should be noted however than many others consider it a very bad feature!). Those of you who have been exposed to languages such as Common LISP or POP11 will not find this is concept too strange, however those of you who have been brought up on languages such as Pascal and ADA may find the perform mechanism horrifying at first glance. As with most language constructs it can be very useful if used judiciously.

Normally, when a message is to be sent to an object, it is specified by the programmer when they write the method. For example:

```
myMethod: anObject
Transcript show: anObject printString.
```

In this example, I have specified that first the object contained in the temporary variable `anObject` will be sent the message `printString` and that the result of this message expression will be used as a parameter with the message `show:` to the object `Transcript`. This is all perfectly normal.

However, in some cases the programmer may not know what messages the object passed to them may respond to, or they may not know what type of message should be sent. This may well be the case with a generalist class which is intended to act as some sort of framework which will be reused and customized by the users of the class.

One way around this problem would be to specify that the user of the class should write their own method (e.g. by using a subclass responsibility message) or should modify the current method specifying what the message should actually be. This second approach, (which would be the case, if we know what the method should do, but were lacking the name of one particular message to send to one particular object), is not really desirable.

In some cases, even the person reusing the class may not know what the message to send is; it may only be possible to determine that at *run time*. To get around this, a programmer might have to write a large case-style statement, which tested the object to see what type it was, and then selected the appropriate message to send depending on that type. Again, this is not really desirable as the number of possibilities may be large and each time a new message was used, the programmer would have to update the method accordingly.

Smalltalk provides a way around this which is referred to as the perform mechanism. The `perform:` message (and those associated with it) can be used to request that an object receive a message which is contained within a variable. For example:

```
anObject perform: someMessage.
```

In this example, the object `anObject` is specified to receive a message `perform:` with a single parameter. This parameter will be the contents of the variable `someMessage`. Thus at compile time,
we cannot tell from this line alone, what action the object will perform. The mechanism essentially allows the method name, as well as the object, to be held in a variable. For example,

```
aVariable := #size.
aCollection perform: aVariable.
```

is equivalent to:

```
aCollection size.
```

The contents of the variable passed with the `perform:` message must be a symbol, otherwise an error will be generated. It must also be a legal message otherwise an error will also be generated. For example, if the variable contained the symbol `#abcd`, then that would be equivalent to evaluating `aCollection abcd`. Unless this message is defined for this class of object, then the message will generate a "not understood" exception (whether it is sent directly or as part of a perform message).

The perform mechanism can also handle methods which take arguments. The arguments are handled by different versions of the perform: message:

1. for methods with one argument use `perform:with`:

2. for methods with two arguments use `perform:with:with`:

3. for methods with three arguments use `perform:with:with:with`:

4. for methods with more than three arguments use `perform:withArguments:` where the arguments are assumed to be in an array.

For example,

```
anObject perform: aSymbol with: anotherObject.
anObject perform: aSymbol with: object1 with: object2.
anObject perform: aSymbol with: object1 with: object2 with: object3.
anObject perform: aSymbol withArguments: aCollection
```

Note that the symbol held in `aSymbol` will be the message selector name. That is, if the message to be sent would normally be written as:

```
anObject at: 2 out: 5.
```

then with the perform mechanism this would be written as:

```
aSymbol := #at:put:
object1 := 2.
object2 := 5.
anObject perform: aSymbol with: object1 with: object2.
```

The perform mechanism is very powerful and provides a great deal of flexibility. However, in most normal situations you should not need to use it. Indeed, there are a number of points to remember when thinking about using the perform mechanism. The first is that it is less efficient than directly sending a message to an object. The second is that it can be more difficult to maintain as it is not immediately obvious what message is being sent to the object. Thirdly, it can be an awful lot harder to debug code containing `perform:` messages. This is because the system back trace displays the message selector name and not display the same chain of message sends that the user of the class expects.

So where should you use it? The situations where you are likely to encounter it are almost all associated with the user interface. However, you should not try to avoid it just because it may be less efficient at run time and more difficult to debug. There may be situations in which the perform mechanism can allow the construction of very powerful and flexible frameworks. The important point to note is that in this situation there may be a trade off between flexibility and efficiency.

## 25.3 The Dependency mechanism

There are a number of different relationships between objects in the Smalltalk environment which we have already looked at. In particular we have considered:
* Inheritance (class to class relationships)

* Instantiation (class to instance)

* Part of or contains (instance to instance relationships)

However, there is another important relationship supported by Smalltalk, this is the dependence relationship. This is where the state or behavior of one object is dependent on the state of another object. For example, Figure 25.1 indicates that there are a set of dependency relationships between the objects A to F.

<figure><img src="image_98.png" /><figcaption>Figure 25.1: Dependency between objects</figcaption></figure>

This figure illustrates that object A is dependent on some aspect of objects B and D. In turn object B is dependent on some aspect of object C and so on.

In Smalltalk dependency is a relationship which can be used to relate two objects such that, as the state of one changes, the state of another automatically changes in an appropriate manner. In such a relationship we say that one object is dependent upon another.

### 25.3.1 Why do we want it?

The reasons for dependency are all down to *change*. That is, we wish to communicate the fact that one object has changed its value to another object which may be interested in either the fact of the change or the new value effected by the change. The dependency mechanism provides a way of communicating such events in a generic implementation independent manner.

An obvious question is “why not just get an object to send messages to those interested in it?” The answer to this is that if you know what objects you want to send messages to, then do so. However, if all you know is that sometime, at a later date, some object may need to know something about the state of an object (but at present we don’t know what that other object might be) then we cannot arrange to send messages to that object - because we don’t know what it will be. However, the dependency mechanism allows any object (whose class is a subclass of `Object`) to be involved in a dependency. A second advantage is that we don’t have to know about the objects interested in the object we are working on. All we need to know is that it might be involved in a dependency relationship and we can let the (hidden) dependency mechanism take care of informing these unknown objects about the updates.

### 25.3.2 How does it work?

As is hinted at in the above section, the dependency mechanism is implemented in the class `Object`. This of course means that, as all classes in Smalltalk are subclasses of `Object`, all objects can be involved in a dependency. You can browse the `Object` class to explore the dependency mechanism. If you use the implementors option off the message window menu you will find that some of the messages are redefined lower down in the class hierarchy. This is purely for internal efficiency or specific implementation reasons - you will still use the mechanism in the same way.

The basic implementation, inherited from `Object`, associates a collection of other objects with an object. This collection holds the objects which are dependent on the object (collectively these objects are known as the objects dependents). For example, in Figure 25.2 the object `ObjectA` has two dependents `ObjectB` and `ObjectC`. The links to the dependent objects are held separately from `ObjectA` in a dependents list. `ObjectA` can access this list via the message dependents.
<figure><img src="image_99.png" /><figcaption>Figure 25.2: An object and its dependents</figcaption></figure>

The message `dependents` can be used to obtain a list of all the dependents of an object (no matter what the class of the object). For example, to obtain a list of the dependents on `ObjectA` we could use:

`ObjectA dependents.`

From the point of view of the actual implementation, the dependents are sometimes held in an instance variable inherited from above (so you can see it in an inspector). This is the case for most of the user interface classes. In other cases the dependents list is held as part of an `IdentityDictionary` in a class variable (this is the default mechanism inherited from `Object`).

### 25.3.3 Constructing dependencies

The `addDependent:` message is used to add an object to another object's dependency list. For example, we can construct the above dependencies using:

```
ObjectA addDependent: ObjectB.
ObjectA addDependent: ObjectC.
```

Note that an object holds (or can access) a list of objects which depend on it. Whereas an object cannot access information about the objects on which it depends. For example, there are no references from `ObjectB` or `ObjectC` back to `ObjectA` in Figure 25.2. Thus an object does not hold a list of objects on which it depends. This may seem a bit strange at first, however, once you gain an understanding of how the dependency mechanism works, hopefully you will see why things are this way round.

It is also possible to remove dependencies once they have been created. This can be done using the `removeDependent:` message. For example:

`ObjectA removeDependent: ObjectB.`

This removes `ObjectB` from the dependency list of `ObjectA`. It should be noted that as an `IdentityDictionary` is used (at least in objects which are instances of classes below `Object`), `ObjectB` must be the same object as was used to create the dependency. It cannot be merely equivalent.

<figure><img src="image_100.png" /><figcaption>Figure 25.3: The dependents message defined in `Object`</figcaption></figure>
You can find the methods which implement `dependents`, `addDependent:` and `removeDependent:` all defined in the class `Object` in the *dependents access protocol* (as illustrated in Figure 25.3). Browse these methods yourself, they will help you to understand how the dependency mechanism works.

### 25.3.4 A simple dependency example

This subsection presents a very simple dependency example, which we will develop further during the chapter. It creates two objects and creates a dependency between them. The objects are instances of a class `DataObject` and `DependentObject`. These classes are direct subclasses of `Object`. Create the classes placing them in an appropriate class category (for example, *dependency demo*). For example:

```python
Object subclass: #DataObject
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'dependency demo'
```

Then evaluate the following in a Workspace:

```python
| temp1 temp2 |
temp1 := DataObject new.
temp2 := DependentObject new.
temp1 addDependent: temp2.
Transcript show: (temp1 dependents) printString.
temp1 inspect.
```

The result of the show message expression, printed in the Transcript, is:

```text
#(a DependentObject)
```

Notice that, although the `dependentObject` instance was printed in the Transcript, no dependents can be found for the `dataObject` in the inspector. Indeed no instance variable called dependents can be found at all.

From the point of view of the class `DataObject`, dependency is an invisible mechanism which works behind the scenes. Of course, this is not really the case. The dependency mechanism has been inherited from `Object` and is implemented via message sends and method executions just like any behavior provided by an object.

### 25.3.5 Making dependency work for you

We have now considered how we construct a dependency relationship. However, we want this relationship to be used to inform the dependent object(s) that a change has occurred in the object on which they depend. That is, we want to tell one object that another object, has changed in some way.

To do this we use two sets of methods. One set is used to state that something has changed. These are called “changed” methods. The other set are used to state what type of update is required. These are called “update” methods. They work as illustrated in Figure 25.4.

<figure><img src="image_101.png" /><figcaption>Figure 25.4: The dependency mechanism in action</figcaption></figure>

Figure 25.4 illustrates the sequence of messages which are sent in response to a change message being sent to an object. That is, when `ObjectA` is sent a changed message (usually by itself) all its dependents are sent an update message. Again from the point of view of `ObjectA` much of this behavior is hidden. In fact so much so that a point of confusion relates to the sending of one message
(i.e. the changed message) and the execution of another method (i.e. the update method). That is, a
programmer defining Objects A, B and C will:

1. send a changed message to ObjectA

2. define an update method in ObjectB and ObjectC.

The confusion here stems from the need to send one message but define another. However, if you think
about how you are linking into the existing dependency framework, it can make more sense. Essentially
the change message is a message to the dep endency mechanism asking it to notify the object's
dependents about a change. The dependency mechanism is inherited and is generic across applications.
However, there is no way that the system developers could know when the change message should be
sent. That is application specific. It is therefore the application developer's responsibility to send the
changed message. For example, you may only want dependents to get told of certain changes, such as
updates to only one field on an input screen etc.

In turn, there is no way that the system developers could have known how you would want the
dependents to update themselves. The update message could indicate that the new value produced by
the originating object should be displayed, that some calculation should be re-performed, or that a
database should be accessed. In fact in the default implementation, inherited from Object, the update
methods do nothing. They are only there so that an error is not generated if a message is sent to an
object which does not have a new update method defined for it.

In the simple `ObjectA`, `ObjectB` and `ObjectC` example above, we would need to specify what `ObjectB` and `ObjectC` should do when `ObjectA` changed. This would require defining our own update methods.

### 25.3.6 The “changed” methods

There are three different changed messages which take zero, one or two parameters. They are:

anObject changed: anAspect with: aParameter.
anObject changed: anAspect.
anObject changed.

The first point to note about the changed messages are that they are sent to the object which has changed in some way. That is, they are used to inform the object that a change has taken place to it and that this change should be passed onto any dependents it may have. That is, the changed message does not effect the change, nor is it sent to the objects which wish to be notified about the change, rather it is telling the changed object that it should notify the dependency mechanism of the change.

Each of the three messages will trigger off the update part of the dependency mechanism. The only
difference between the messages relates to the amount of information provided. The first message can
be used to not only tell the dependent objects what aspect of the object changed but also what the new
value produced by the change was. For example, the aspect that has been modified might be an instance
variable and the parameter the new value of the instance variable. In turn the second message only tells
the dependents what aspect has changed, while the third only in forms the dependents that some change
has taken place. Therefore the simplest changed message (and the one with the least information) is the
changed message. This can be useful when you want to make sure that the dependents assume
nothing about the object to which the change is happening.

A point to note is that the way that these messages are implemented is that the changed method calls
the changed: method with `nil` as a parameter. In turn this method calls the changed:with:
method with the parameter passed to it and a `nil` parameter. Thus if you wish to modify the way that
the dependency mechanism works, the changed side is encapsulated within the changed:with:
method.

### 25.3.7 The “update” methods

There are three different versions of the update messages which take one, two or three parameters. They
are:

update: anAspect with: aParameter from: anObject
update: anAspect with: aParameter
update: anAspect
These messages are sent automatically by the dependency mechanism in response to a change of data message being sent to the object, on which the object they are sent to depends. That is, if `ObjectA` is sent a changed message, one of the above will be sent to `ObjectB`. Which message appears to be sent depends on which method has been defined in `ObjectB`. This is because the method calling process is implemented such that the first message sent to the object will be `update:with:from:`. Unless this is overwritten, this method calls `update:with:`. In turn the `update:with:` method calls the `update:` method. This method by default does nothing. Therefore, if you define any one of these three methods, that method will be executed.

This means that the developer can decide how much information the dependent object wishes to work with. The parameter values that the developer can work with are:

* `anAspect` the value used for `anAspect` in the changed message or `nil`.

* `aParameter` the value used for `aParameter` in the changed message or `nil`.

* `anObject` the object which received the changed message.

### 25.3.8 Extending the dependency example

This section will provide an example of how the dependency mechanism works. We shall use the `DataObject` and `DependentObject` classes defined back in section 25.3.4.

The first thing we shall do is to define a couple of instance variables in `DataObject`. These variables will be `age`, `name` and `address`. For example:

<figure><img src="image_102.png" /></figure>

Next we shall define an `updater` method for each of these instance variables. For example, in Figure 25.5, we have defined a new method `age:` which sets the `age` instance variable. It then informs the object that we have changed its age and that this fact should be passed onto its dependents (i.e. self changed: `#age`). This is a typical usage of a changed message. That is, it is sent to self informing self about the change which has taken place. It is very poor style to have one object send an instance of `DataObject` the message `age:`, followed by the changed message!

Next we shall define how an instance of `DependentObject` should respond to the change in a `DataObject`. That is we will define one of the update methods. The method we will define is the `update:` method. This method is placed in a protocol called `updating`. The actual method is illustrated in Figure 25.6. As this is just a simple example, all that this does is to print a string in the `Transcript` which illustrates the change which has occurred to a `DataObject` instance.

<figure><img src="image_103.png" /><figcaption>Figure 25.5: Defining an `updater` for `age`</figcaption></figure>

We are now ready to try out this simple example. To do this we will use the source code previously typed into the `Transcript` (minus the last `inspect` statement). To this we will add three statements which set the `dataObject`'s `age`, `name` and `address`:
```
| temp1 temp2|
temp1 := DataObject new.
temp2 := DependentObject new.
temp1 addDependent: temp2.
Transcript show: (temp1 dependents) printString.
temp1 age: 32.
temp1 name: 'John'.
temp1 address: 'C47'.
```

The result of evaluating this code, is that the following statements are printed in the Transcript:

```
#(a DependentObject)
The object I am dependent on has changed its #age
The object I am dependent on has changed its #name
The object I am dependent on has changed its #address
```

Notice that the `dependentObject` has been informed of the changes to the `dataObject`'s `age`, `name` and `address`, even though we have not defined a method which does this directly.

<figure><img src="image_104.png" /><figcaption>Figure 25.6: Defining the update: method in DependentObject</figcaption></figure>

### 25.3.9 Discussion

In the simple example presented in section 25.3.8, you should note (and understand) the following points:

1. In `DataObject` we did not have a reference to, nor did we know anything about, a `DependentObject`.

2. The `DependentObject` does not reference a `DataObject` anywhere internally.

3. The link between `temp1` and `temp2` was made externally to either object.

It should also be noted that it can be more difficult to debug and maintain relationships which have been implemented using the dependency mechanism (as the message chain is partly hidden from the developer). Therefore care should again be exercised in its use.

## 25.4 Summary

This chapter has introduced the perform mechanism and the dependency mechanism. The former is relatively straight forward but should be used with care. The latter is more complex, but extensive use is made of it by the user interface classes. You should therefore gain some experience in using it. For example, have a go at modifying the `FinancialManager` example, add a monitor class which will send the user a message if their bank balance dips below a certain threshold.