Step 2: Refine
Detail the steps in each requirement via scenarios captured in Activity Diagrams. Add other diagrams where they shed light on the activities.
In this step, you’ll describe in more detail what your system will do within each use case. Any given use case will have at least one scenario that describes a particular way the use case may be carried out. This is the Primary Scenario (or less formally, the Happy Path), and reflects what happens in the normal, expected case. If there are decisions or exceptions or errors that must be handled within the use case, these will dictate Alternate Scenarios (or Exception Paths). The Primary and Alternate Scenarios shall be captured in an Activity Diagram.
An Activity Diagram is a way of depicting a set of steps of operation within your system, and is often attached to a use case (as you’ll do in Five-Step UML). It has much in common with old-fashioned flowcharts—in essence, the Activity Diagram notation is a simplification of the flowchart notation—and thus should be comprehensible to a very wide audience. I like to call Activity Diagrams “the revenge of the flowchart”; in my more cynical moments, I wonder if teaching UML is a penance I must serve for having such disdain for flowcharts as a younger developer. But in fact, Activity Diagrams both supplement and simplify the old-fashioned flowchart notation, allowing for more useful diagrams than anything I saw in my youth.UML Notation
At its core, a simple Activity Diagram consists of eight elements: initial states, terminal states, activities, transitions, branches, merges, forks, and joins.
An initial state denotes where the scenarios in your Activity Diagram begin. Think of it as the entry point to your diagram. There should be exactly one initial state in a well-formed Activity Diagram: if there were no initial state or multiple initial states, readers of your diagram would not know where to look first.
In an Activity Diagram, an initial state appears as a solid black dot.
A terminal state denotes the completion of the scenarios in your Activity Diagram. This is the point to which the diagram leads. There should be zero or one in a well-formed Activity Diagram: one if the scenarios have a definite end, zero if the scenarios cycle repeatedly with no defined termination. This rule is often broken, because it can be far less cluttered to have multiple terminal states in a diagram than to have arrows that snake all over the page to reach a single terminal state.
In an Activity Diagram, a terminal state appears as a solid black dot in a larger circle. (Think of it as the “target” of the diagram, to which all paths lead.)
An activity (or more formally, an activity state) depicts an action taken by your system. It’s a brief description that often represents more detailed actions underneath. (Later, as you refine your design further, you’ll create more detailed activities that approach the level of code.)
In an Activity Diagram, an activity appears as a rectangle with semicircular ends. (I call this the “capsule shape,” because it resembles a cold capsule. Others call it the “hotdog shape” for similar reasons.)
Why are activities also called “activity states”? Why “initial states” and “terminal states”? Well, it’s time for a little history lesson.
Activity Diagrams are a fairly recent addition to UML; but before there were Activity Diagrams, there were Statechart Diagrams. A Statechart Diagram depicts states of the system or part of the system, along with how events cause changes in those states. They’re a very powerful tool, especially in modeling real-time systems and mechanical interfaces.
Now hard-core state modelers are a strange bunch, at first. They will get quite adamant that Print Invoice isn’t a state, but Print ing Invoice is. While they can get a bit pedantic about this, their point is fundamentally correct: a state isn’t something a system does; it is a description of a system, and may include a description of what the system is doing. States are also (potentially) interrupt ible by events. If you don’t keep these ideas in mind, you may create Statechart Diagrams that are more confusing than helpful.
But less hard-core state modelers—and especially people who had never seen Statechart Diagrams before, and didn’t get the point—sat down and used State- chart Diagrams to build glorified flowcharts. UML already had Sequence Diagrams, which could be used like flowcharts in some ways; but it was clear that a flowchart-like diagram was needed by many modelers.So Activity Diagrams were added to UML; but they were first introduced as a modification of the Statechart Diagram. You still see some of this legacy in the nomenclature.
Expect to see some minor changes to Activity Diagrams in UML 2.0. One of these changes will be to formally separate Activity Diagrams from Statechart Diagrams. There will also be changes to better support business modeling using Activity Diagrams.
A transition represents the flow of control in your system from one activity to another. You may also create transitions from an initial state to an activity or from an activity to a terminal state; and (as you’ll see later) you may also create transitions to and from branches, merges, forks, and joins. The transitions form the “flow” of your scenarios in terms of steps being carried out. They may also indicate events that cause the system to stop one activity and begin another. For example, an activity of Poll for Input may end when an Input Received transition transfers control to a Process Input activity.
In an Activity Diagram, a transition appears as an arrow indicating the flow of control. It may optionally include a label that denotes the event that causes the transition; and (as you’ll see in the next section) it may also include a guard condition enclosed in square brackets.
A branch represents a decision in your system, a point where you must select exactly one of multiple possible transitions to follow. Each transition leading from the branch must have a guard condition, an expression which evaluates to true or false; and in a well-formed Activity Diagram, exactly one guard condition may evaluate to true each time control reaches a particular branch.
In an Activity Diagram, a branch appears as a diamond with one or more transitions entering it and two or more transitions leaving it (each with a guard condition).
A merge is the opposite of a branch: a place where two or more alternate flows of control rejoin and continue as a single flow. You may see diagrams in which a merge is also a branch. For example, the start of a loop is often modeled as a branch (because flow either enters the loop body or skips it) and as a merge (because you can enter the loop start from before the loop or by returning to the start of the loop).
In an Activity Diagram, a merge appears as a diamond with two or more transitions entering it and one or more transitions leaving it.
A fork is a way to show multiple activities that can occur in any order, or even simultaneously. It represents a single flow that splits into multiple flows, such as multithreaded programs or multiple processes.
In an Activity Diagram, a fork appears as a thick horizontal or vertical line with one transition entering it and two or more transitions leaving it. The outgoing transitions from a fork are commonly called threads.
Threads vs. ThreadsIf you come to UML from a parallel processing background, “threads” may imply “multithreaded programming” to you, since that’s a common technology for implementing parallel behavior. This is one example of code you might model with UML threads; but threads in UML are more general than that. A thread simply indicates a subflow of control that begins at a fork, ends at a join, and may occur before, after, or simultaneously with other threads of control.
During Step 2 of Five-Step UML, the refining stage, you’ll seldom need to think about forks (or about joins, as described next). Unless the requirements specifically state that certain activities must occur simultaneously, forking is more of an implementation issue that may only confuse the gathering of requirements. During design (Step 4), forks and joins will be useful ways to depict simultaneous activities.
A join is the opposite of a fork: a place where two or more threads rejoin to form a single flow. It represents the completion of all activities within each of the threads.
In an Activity Diagram, a join appears as a thick horizontal or vertical line with two or more threads entering it and one transition leaving it.
Given these elements, then, an Activity Diagram depicts the transitions between activities, branches, merges, forks, and joins that collectively depict the playing out of one or more scenarios. Figure 2-5 is an Activity Diagram that depicts the scenarios within the Gather Pet Info use case from the KMS case study.
In this example, you can see four scenarios:
So far, I’ve only identified three Alternate Scenarios in Figure 2-5, and already it is quite complex. Imagine how much worse it could get with many more Alternate Scenarios. If you want to communicate with this diagram, you might want to simplify it. (Note the emphasis on might: this is an example, not necessarily the only solution.)
You can simplify the diagram by using subactivity states. In an Activity Diagram, a subactivity state is an activity that contains a more detailed Activity Diagram. This contained diagram can be depicted on the main diagram (though that seldom simplifies the picture), or it can be shown on a separate page (for a paper model), or it can be placed inside the activity (for an electronic model).
So Figure 2-5 might be simplified as in Figure 2-6, where the new states, Get Pet and Get Owner, are subactivity states.
Then the Activity Diagrams for the Get Owner and Get Pet subactivity states might be depicted as in Figures 2-7 and 2-8.
Note how each contained Activity Diagram has its own initial and terminal state, just as the primary diagram does.
In later examples in this chapter, we’ll work from Figure 2-5, not from Figures 2-6 to 2-8. Figure 2-5 makes a better example to build upon. But I think you can make a good case that the latter diagrams are easier to comprehend.
A Better Approach?
As a rule, it’s better to identify the subactivity states in advance, rather than defining them after the fact. It’s often easier to draw the diagrams this way (especially with some UML tools), and you do less rework. So I encourage you: if you foresee a need for subactivity states, add them in from the start. If you can do that, more power to you. You’re a “big picture” thinker with a lot of foresight. Me? I’m a detail thinker, and I almost never can foresee the need for subactivity states. Sometimes, I wish I could.There is one exception to my lack of foresight: I have learned from hard experience that it’s pretty much always easier to understand forks and joins and threads if the body of each thread is a single activity. That usually means it’s a subactivity state.
Exercise 202: Refine Your System Definition with Activity Diagrams:
blog comments powered by Disqus