3 USE CASES  23/10/97 3.5 Use cases through the development

3.5 Use cases through the development

Planning By the end of the Elaboration phase of Objectory, or by the end of analysis, if you prefer, we should (in an ideal world) have a complete list of all the use cases for our system, together with:

• a good idea of what each means

• an understanding of who wants each and how much

• knowledge of which use cases carry most risk, where risk may be:

– requirements risk: higher if it’s more likely that we haven’t got the requirements quite right yet, or that they may change

– technological risks: higher if we’re using some unfamiliar component collection, system, compiler, database, combination of these, etc.

– skills risk: closely related to technological risk; higher the more areas there are where developers aren’t very experienced.

(or others, e.g. maybe “safety risk” if you see what I mean, in a safety critical system).

• a plan for how long it should take to implement each use case (ha ha – they won’t be independent, will they?)

Every use case will be implemented in some iteration of the system. Indeed, as I mentioned before, Objectory may go further and require the converse, that every iteration of the system delivers some use case. At any rate, by this stage we should have planned the iterations, and should know (at least provisionally) which use cases will appear in which iterations. So in what order do we tackle the use cases?

Political aspects Remember that 25% of systems that were never delivered? It wasn’t because the developers decided to go on holiday instead: it was because the project got cancelled. That is, someone somewhere decided it wasn’t worth going ahead with. How do we stop that happening to our project?

If we’ve captured requirements in terms of use cases and actors, we are likely to have a very good idea of which use cases – i.e. which aspects of the system’s behaviour – are most important to which people. (And here I really do mean people, not actors.) So, other things being equal, we want to be able to demonstrate the system doing something valuable to the most influential people first. As soon as possible, we want to make sure that everyone who was the power to scupper the system (and remember this may include people who don’t have formal power in the organisation!) has some good reason not to do so. This means that they must see that if the system is completed and delivered, they will get something they want, which they will lose if they get the project cancelled.

Comparing use cases which are important to a given person, of course other things being equal we should implement the highest priority ones first.

This may all sound very cynical, but I think you’ll find it’s sound.

3 USE CASES  23/10/97  3.6 Relations between use cases

Politics

If we capture requirements in terms of use cases, we should understand what is important to whom.

Make sure system delivers added value:

• soon

• to all the people who might scupper it

• in every iteration

Result: the project isn’t cancelled. Supposedly...

Slide 28

Technical aspects Another criterion, which may conflict with the ones above, is that we should deliver the use cases associated with highest risk first, so as to tackle the greatest risks when we still have contingency to tackle them, and so that we don’t get tied into a design which will not allow us to deal with the hardest (highest risk) use cases.

By the way, compare and contrast this with Booch’s approving quotation of Heinlein: “When faced with a problem you do not understand, do any part of it you do understand, then look at it again”.

3.6 Relations between use cases

There are two kinds of situations in which we may want to document a relationship between two use cases. On the use case diagram, this is shown as an open-headed arrow between two use case ellipses. The two cases are distinguished by being given different stereotypes: the first is given the stereotype ≪uses≫, the second is given the stereotype ≪extends≫.

We call a use case in our use case model abstract when it does not interact with any actor in our diagram; in this case, it must be that the abstract use case is related to another use case in one of the ways we will discuss.

Notice that, even though Rose calls relations between use cases “generalisation relationships”, this is very different from generalisation between classes. Do not confuse the two. Note also that, as we shall discuss, a use case may be associated with a component which is being reused. In this case it may well be that the use case will sometimes interact with an actor, whereas at other times it will be another use case that plays the part of the actor with respect to the component use case. That is, a use case is abstract with respect to the current use case model; this is not an absolute fact about the use case itself.

Use case reuse: ≪uses≫

Slide 29

3.6.1 Use cases for reuse: ≪uses≫

The most vital case is when we are able to factor out common behaviour from two or more of our original use cases, or (better still) when we see that we can implement part of one of our use cases by using a component (analysis, design or implemented). We document the shared or reused part of the work-unit as a use case in its own right, and document which other use cases use this new use case.

As in the example above, we show this by an arrow from the “user” use case to the “used” use case labelled (with the stereotype) ≪uses≫, to represent that the source use case makes use of the target use case.

The concept of use differs according to what stage of the development we’re at. It may be a specification concept: these use cases have common functionality which we only want to describe once. Of course, we’d like to be able to follow this through by only implementing it (at most) once: but even if there are good reasons for this not being possible, it may still be helpful to show the common behaviour in early diagrams, separating them later. More commonly, we will discover common behaviour as we perform analysis and design.

If we are to be serious about doing component-based design, it’s essential to think about using components as early as possible (certainly during the elaboration phase of Objectory). There are several reasons for this.

Firstly, we will certainly need to adapt the way we describe the use cases to match the available components; we may well need to negotiate small changes in the requirements themselves, in order to make good use of available components. (This may sound radical: but consider the parallel case in, say, architecture. You are unlikely to be asked exactly what size and shape you want your doors. More likely, you will be asked to choose from a range of available shapes and sizes, each with its own cost; if it’s possible at all to specify that you want a round green door with a brass knob, you can expect to pay a much higher price for it, and to wait longer, than if you want something standard. It seems reasonable to say that if the software industry is to make the shift to component-based engineering, our approach to flexibility of requirements will similarly have to change. Or do you think that the inherent flexibility of software will be enough to mitigate this?)

Secondly, if we will be able to implement some reqirement using a component, we want to know that before we expend time and effort working out how to build it ourselves.

Jacobson describes the ≪uses≫ relationship as being a kind of inheritance. I think this is highly misleading: the only similarity is really that both inheritance of one class by another and use of one use case by another are forms of reuse.

We meet here for the first time a question which will recur: when we use a component which we don’t have to develop, do we show it on our design diagrams? Or do we treat it as though it were part of the programming language, and just use it? Either is possible; I suspect we will want to do it both ways depending on what the component is. For example, suppose we use an OrderedCollection class from a library of collection classes, in order to order the items on a bank statement. I doubt that there’s any value in showing “Order Things” as part of a use case. But if we use a more complex component – say, using “Transfer Financial Instrument” to implement “Transfer Money” for a bank customer – we probably do want to show it. Yet again, it’s a judgement call.

Naturally if we are planning to develop a reusable component that will include some, but not all, of the functionality of a given use case, it will also make sense to describe the proposed component clearly by its own use case, in this way.

Note on ≪uses≫ and ≪extends≫ in Rose Suppose you have in a Rose use case diagram two use cases, shown as ellipses, and you want to related them by ≪uses≫ or ≪extends≫. Here’s how:

1. Use the open-headed arrow tool from the Use Case diagram toolbar to get the right kind of arrow between the uses cases. (Make sure you have it going in the right direction!)

2. Get up the specification window for the new arrow. You can do this by double-clicking on the arrow, or by choosing Open Specification... from the arrow’s “shortcut menu”, the popup menu that appears when you right-click on the arrow.

3. Record whether the relationship is a ≪uses≫ or a ≪extends≫ one, by entering “extends” or “uses” in the Stereotype box of the specification window of the relation. (You’ll only have to type “uses” or “extends” once; after that these options will appear on the pull-down menu attached to the Stereotype box. Why do you have to type them even once, i.e. why aren’t they in the menu by default? Search me.)

4. Click OK on the specification window to dismiss it

5. Get Rose to display the stereotype ≪uses≫ or ≪extends≫ on the diagram, by choosing Show Stereotype from the arrow’s shortcut menu (right click on the arrow).

3.6.2 Separating variant behaviour: ≪extends≫

If a use case incorporates two or more scenarios – that is, several different things may happen depending on circumstances – we may decide that it would be clearer to show these as a main case and one or more subsidiary cases. When to do this is a matter of judgement, since we can always show variant cases in one use case. For example, the Capture Deal use case represents the straightforward case in which the deal can go ahead. Alternatively, it may turn out to be the case that the trader’s limits have been exceeded (in the post Nick Leeson world), in which case the deal will not go ahead. We use the ≪extends≫ arrow from the less central case to the central case, as shown in the example above.

3 USE CASES  23/10/97 3.7 Possible problems with use cases

\underline{≪extends≫}

Slide 30

In UML, we may optionally document an extension point of the central use case, which is a formal or informal idea of the point in the description of the central use case at which it is decided whether or not to go into the extension use case. (For example, the extension point in the Capture Deal example might be called Check Limits.) UML permits the extension point to be recorded in the ellipse for the central use case. However, Rose does not appear to support this, so we won’t use this feature in this course.

3.7 Possible problems with use cases

As we’ve seen, use cases can help with some of the most difficult aspects of system development, namely requirements capture, planning, iteration management and test planning. However, they do also have shortcomings.

Shortcomings

1. Danger of losing OO: plan focus on use cases may encourage a functional view of the system

2. Danger of mistaking design for requirements

3. Danger of missing requirements

Use use cases to guide a disciplined OO development – don’t let them be the development.

Slide 31

In more detail:

1. Focussing on use cases may encourage developers to lose sight of the architecture of the system and of the static object structure, in the rush somehow to deliver the required use case. Later, if the functionality of use case has already been developed, it may be hard to justify the time to modify the design to maintain its integrity with respect to subsequent use cases. We may end up back were we started, developing a top-down function oriented unmaintainable inflexible system.

2. Capturing requirements by use cases may encourage developers to think too operationally: users are likely to describe the use case as a very concrete sequence of interactions with the system which is one way, not the only way, of achieving their real goal. It’s important that developers distinguish between requirements and candidate designs.

3. It’s a mistake to think that use case based requirement capture, examining the needs of each actor in turn, will automatically identify all requirements. Consider, for example, a utility company that needs to send out quarterly bills to its customers. How will you identify the use case “send out bills”? The customers aren’t going to identify this as something they need! The billing department isn’t involved in the process, since it’s automatic every quarter. Is the Calendar the actor who needs it? Hmm...

4 THE CLASS MODEL  23/10/97

4 The class model

Now we come to an element of UML which has a counterpart in more or less every OO modelling language, because it has a fundamental importance – the class diagrams. (Found in Rose under Logical Model.)

There is an important sense in which the notion of class is an engineering convenience added on to the more fundamental notion of object. So let’s (re)visit that notion first.

4.1 Objects and messages

What I say here applies to standard modern class-based object oriented languages such as Java and C++. Most of it also applies to Smalltalk, Eiffel, CLOS, Perl5 and other objectoriented languages you may meet: but a detailed comparison of OOPLs is outside the scope of this course. The footnotes and paragraphs in brackets are intended to be optional reading!

An object understands certain messages, which is to say that it can receive the messages and act on them. An object usually also has some internal data, in the form of a collection of named instance variables or attributes. In each object, there is a value associated with each instance variable; the value may change over the lifetime of the object. The set of instance variables, though, and the set of messages the object understands, is fixed. The way in which an object reacts to a message may depend on the current values of its instance variables: in this way, even when (as usual) the outside world is not granted direct access to the instance variables, it may be indirectly affected by their values. That is, the values of the instance variables are the state of the object.

For example, consider an object which we’ll call myClock, which understands the messages reportTime and resetTimeTo(07:43), resetTimeTo(12:30), indeed more generally resetTimeTo(newTime) for any sensible value of newTime. It may advertise this by giving an interface which says that it accepts messages of the form reportTime() and resetTimeTo(newTime: Time) where Time is a type or a class whose elements are the sensible values of newTime we mentioned.

How does it implement this functionality? The outside world doesn’t need to know; but perhaps it has an instance variable time, the value of which is returned by the object in response to message reportTime, and which is reset to newTime when the object receives the message resetTimeTo(newTime), where the value newTime could take any appropriate value. Or perhaps it passes these messages on to some other object which it knows about and has them deal with them.

From this example we see some things about messages. A message includes a keyword (sometimes called a selector); here we’ve seen the selectors reportTime and resetTimeTo. A message may, but need not, include one or more arguments, which are values being given to the object just as values are passed into a function in a normal function call. In the example, 07:43 is an argument forming part of the message resetTimeTo(07:43). Usually, for a given selector there is a single “correct” number of arguments which should be present in a message which starts with that selector; we will not expect myClock to understand resetTimeTo with no argument, or resetTimeTo(4, 12:30) etc. The acceptable values of arguments are also laid down^{1} in the interface of the object.

(Why did I say “Usually”? Because in many languages, such as C++ and Java, a message can be overloaded – that is, an object may understand messages with the same selector but with different collections of arguments. In our example, perhaps myClock might understand both resetTimeTo (newTime) and resetTimeTo with no argument; perhaps in the latter case it resets its time to midnight. This is a very useful technique, because the reader of the program will naturally assume that an object responds very similarly to messages with the same selector, which can speed understanding of your code provided this assumption is justified! You must be careful only to use overloaded messages to implement functionality which is semantically essentially the same, in some sense.)

This sounds just like the idea of a function signature: the difference is that when you send a message to an object, you do not in general know what code will be executed as a

1by giving the class, or sometimes just the interface, which the object passed as an argument should have, or its type if it should belong to a basic type like int or bool – the details here are language dependent.

4 THE CLASS MODEL  23/10/97  4.2 Visibility

result, because that information is encapsulated.

4.2 Visibility

Often, it isn’t appropriate for every object in the system to be allowed to send any message to an object that the object could possibly understand. The object’s public interface defines which messages it will accept. It may send itself other messages (defined in its private interface), but the outside world may only send the ones in the public interface. When I say “interface” in future I’ll mean the public interface.

(We’ll probably mention Java and C++’s “protected” visibility later.)

4.3 Classes

In class-based object oriented languages, every object belongs to a class, and the [most specific: see note on Terminology below] class of an object determines its interface. In fact more is true: even the way in which an object reacts to a message is determined by the object’s class, together with the values of the object’s instance variables. The set of instance variables which an object has is determined by its class, although of course the values taken by those instance variables are not determined by the class, but may vary. For example, perhaps myClock belongs to class Clock, which specifies that objects belonging to Clock have an instance variable called time and specifies how they react to the messages we described. We call the process of creating a new object belonging to class Clock as instantiating class Clock, and we call the resulting object an instance of class Clock – this, of course, is why the variables whose values belong to the object and may vary over its lifetime are called instance variables.

In UML, a class is shown in a class diagram as a rectangle giving its name, attributes (names of instance variables) and operations (messages for which our class defines some behaviour – rather than inheriting it: we’ll come back to this). We can put symbols by each attribute or operation to show its visibility, but in this instance I haven’t shown them. (In Rose you can control whether this is shown, and do other things like adding new attributes and operaions, using the class’s Shortcut menu.)

This and later examples in this section are drawn from Fowler Fig 4.1.

4 THE CLASS MODEL  23/10/97  4.3 Classes

A class Customer

Slide 32

4.3.1 Digression: Why have classes?

Why not just have objects, which have state, behaviour and identity as we require?

Classes in object oriented languages serve two purposes. First, they are a convenient way of describing a collection (a class) of objects which have the same properties. Object oriented programming languages can use classes essentially just as a convenience. For example, there is no need to store a copy of the code representing an object’s behaviour in every object, even though conceptually we think of every object as encapsulating its own code. Instead, compilers create a single representation of a class, and allow objects of that class to access the class representation to get the code they need.

You can see that there are other ways of achieving this convenience; for example, we could define a prototypical object, and then define other objects as being “like that one, but with these different values”. Again, we would only need to keep one copy of the code. This is roughly what delegation based object oriented languages such as Self do. However, it now seems clear (famous last words?) that delegation based languages will end up being a historical dead-end: classes achieve the same aims more conveniently.

Secondly, in most modern OO languages, classes are used in the same way that types are used in many other languages, to specify what values are acceptable in given contexts, and thus to allow the compiler – and the human reader – to check that none of a large class of errors occurs in the program.

Often people thing of classes and types as being the same thing – indeed I may slip into that way of talking myself; it’s convenient, and not often misleading. However, it’s wrong. Remember that a class defines not only what messages an object understands – which is really all you should need to know in order to check whether it is acceptable in some context. It would be good to go into this in more depth, and to talk about interfaces as used in Java, which separate out the specification of an object’s interface from its implementation – but we almost certainly won’t have time.

4 THE CLASS MODEL  23/10/97  4.4 Relationships between classes

( Subdigression on absence of barriers between objects of the same class Often, as in C++ and Java, encapsulation does not work between objects of the same class. In our example, suppose that myClock (and so, all other objects of class Clock) also understands the message synchroniseWith(aClock), where the argument aClock is another object of class Clock, and that they respond to this message by setting their own time to the time of aClock. We would not expect myClock to be able to access the private parts of aClock, but in languages like Java and C++ it can, simply because they happen to belong to the same class. That is, code implementing the behaviour of myClock can access the internals of every object of class Clock that it gets to know about, not just myClock’s internals. There’s a sense in which this is reasonable from a software engineering perspective; remember that encapsulation – which normally prevents code implementing an object’s behaviour from accessing the internals of another object – has as its primary purpose preventing changes to one part of the system from causing changes to be made to other parts of the system. Now, if the internals of aClock change, it must be because of changes made to the code of the Clock class. But if code implementing myClock’s behaviour takes advantage of knowledge about the internals of aClock, that taking advantage must also happen in Clock class code. If we’re editing the code of the Clock class anyway, it isn’t too much extra effort to change more of the code in the same class in order to fix the implementation of the synchronizeWith method – in particular, no extra recompilations will be required because of this absence of a barrier between myClock and aClock. However, taking advantage of this does potentially increase the number of places where changes may have to be made in such a case. You can always avoid taking advantage of it, simply by making more information about Clock public; but that has its own problems. Judge each case carefully on its merits.)

)

4.4 Relationships between classes

We want to capture the coupling in our design: that is, the places where one module (here, class) depends on another. There’s a variety of ways in which this can arise.

4.4.1 Association

The main one is when there’s an association between two classes: this means roughly that some object of one of the classes needs to “know” about the existence of the other class. (And so in particular, might be affected by changes to that class.)

For example, this might be because some object of one class:

• sends a message to an object of the other class, or

• creates an object of the other class, or

• has the other class as the type of a message argument, or

• has the other class as the type of an instance variable

Association between classes

Slide 33

We can (and usually should) name the association; here I’ve called the association places because a customer places an order. UML 1.1 permits the name of an association to contain a small black triangle to indicate in which direction the associated is to be read, so I could have written places⊳ (with the triangle filled in, can’t off-hand find the L^{A}TEXfor this!) to indicate that we mean “a customer places an order” rather than “an order places a customer”. However, Rose doesn’t seem to support this notation, so we won’t use it. Alternatively, we can name the roles that each class plays in the association. If we do this (in Rose, set them in the assocation’s specification window) the role played by the Customer in the association (say placer, perhaps) will appear on the arrow near the Customer end, and the role played by the order (placee?!) will appear near the Order end. Normally we will simply name the association, and the direction will be obvious from the context.

Multiplicity An association between classes exists whenever an association between two objects of the classes exists. Indeed, you can think of the classes as sets of objects, and of the association as a relation on those sets, if you like. We can sensibly ask: given an object in one class, how many objects in the other might be associated with it? (Or, what are the possible values of |{y : xRy}| as x varies, and of |{x : xRy}| as y varies?)

In the example, I showed a 1 at the Customer end of the association places, because every order (that is, every object of class Order) is associated by places with just one customer (object of class Customer), namely, the customer who placed that particular order. On the other hand, a customer may have placed any number, including 0, of the orders in our system (if 0, maybe they placed an order in the past, or asked for a catalogue, or otherwise got onto our system: depends on information I haven’t specified). So the multiplicity on the Order end is 0… * (UML’s choice of notation. or at least Rose’s. Horrible, but there it is.) As you see, we can specify an exact number by writing it, a range of numbers using . . .. We can also specify a choice of multiplicities by giving a comma separated list of multiplicities: for example 3, 12 … 15, 901 … ∗ is an unlikely multiplicity saying that there can be exactly 3, or else between 12 and 15 (inclusive) or else at least 901, of whatever it is.