Figure 2-36 shows the Activity Diagrams for Get or Add Owner, with logic added to handle implementation exceptions.
Looking at Figure 2-36, you may think, “Hmmm, that doesn’t tell me much. How can the caller tell the difference between found or added and not added?”
Good question! There’s no UML convention for this, so instead, I’m going to break a UML rule to make this clearer—the rule that says a well-formed Activity Diagram should have only one terminal state. In component Activity Diagrams like these, I like to indicate different possible return values via different terminal states, as in Figure 2-37.
Now, in the Check In Form, we have an action, Get Owner Name, but we have no specifics on how that name is to be entered: first and last names as one string, first and last names as separate strings, first and middle and last names, etc. So how are we going to handle that?
“How Did I Miss That?”
You may find that, as you try to refine a component use case, there are business rules implied but not specified within your requirements model, for example, our confusion over how to correctly enter information for the Get Owner Name action.
Now you could argue that I just didn’t do a very good job of gathering and documenting and modeling the requirements for this project. And since this is simply an example project with an example model created to instruct you readers, you might even be right; but that would miss the point.
The point is simply this: Requirements work is never done. Period. For that matter, neither is architecture work, nor design work, nor coding work, nor testing work, nor documentation work, nor maintenance work (and certainly not management work). But the reason why the other tasks are never done is that, until the project is gone and no longer supported, new requirements keep trickling in.
You don’t believe me? Good! Skepticism is healthy for a developer. But let me try to persuade you by bringing in some outside help.
Both of these attitudes fail to acknowledge the reality that it is impossible to know all the requirements early in the project and that requirements will undoubtedly change over time.
—Karl E. Wiegers10
With rare exceptions, the requirements must change as the software job progresses. Just by writing a program, we change our perceptions of the task. The ripple of change starts with the requirements and continues through implementation, initial use, and even through later enhancement. Even if we could plan for all this development learning, the computer implementation of human tasks changes the applications themselves. Requirements by their very nature cannot be firm because we cannot anticipate the ways the tasks will change when they are automated.
—Watts S. Humphrey11
While each iteration is a sweep through requirements, analysis, design, implementation, and test workflows, the iterations have different emphases in different phases . . . . During the inception and elaboration phases, most of the effort is directed toward capturing requirements and preliminary analysis and design. During construction, emphasis shifts to detailed design, implementation, and testing.
—The Three Amigos12
For many years, the holy grail of requirements management has been to collect a set of requirements—scrubbed, minimally specified, or otherwise—encase them in permafrost, and then build a complete product design, implementation, documentation, and quality assurance atop them. Unfortunately for developers and their ulcers, projects that have successfully frozen their requirements have proven to be almost as hard to find as the Holy Grail itself.
Give up yet? If not, I can just keep pulling books off my shelf and citing experts who all agree: the Waterfall Model is dead. (I’ll discuss this further, along with different development models, in Chapter 12.) As McConnell said, requirements freeze was an unattainable holy grail, and teams that didn’t accept this up front and plan to deal with it were in for a rude awakening. Modern software processes and methodologies are all predicated on a recognition that requirements will evolve, so you had better have a means to deal with the changes. In Model-Driven Development (Five-Step UML being a simple example), the means to deal with requirements changes are twofold.
First, there is a place in your model for the newly discovered requirements, and the model has to be the central artifact. When you discover a new requirement during design (or implementation, or whenever), make sure you incorporate it back into the requirements model and trace through its implications just as you did during analysis. You have a process that works; don’t give up on it late in the game, because that’s when you need it most!
The second way you handle change in Model-Driven Development is the com mon way found in many modern processes: iteration through the same core activities, over many parts of the problem and at many scales of abstraction. In Model-Driven Development, of course, those core activities are the Five Steps.
In this case, we could decide that the process of Get Owner Name consists of getting a first name, getting a last name, and verifying that neither is blank. This is shown in Figure 2-38.
However, this diagram reveals a basic problem with modeling user interfaces, especially the modern Graphical User Interface (GUI) paradigm: to the extent possible, users are permitted to do work in an order that makes sense to them, not in an order specified by the system. If a form has a First Name field and a Last Name field, nothing stops the user from filling in the last name first, perhaps because that’s what they’re given first. The more a system allows users to work the way they wish, the more satisfied the users are.
Yet a diagram like Figure 2-38 may imply to developers that users must enter the first name first; and thus they may implement this constraint, a constraint that will take time and effort and money and annoy the users. Talk about a lose-lose proposition!
So how can we convey the freedom we want the users to have? There are a number of legal but less than satisfying approaches.
First, we could model every possible path that users might follow. This pretty much never works: there’s too much redundancy and too much clutter, as in Figure 2-39.
As an alternative to this, we could model the user selection with forks and joins and threads. Recall that a fork is a way to show multiple activities that can occur in any order, or even simultaneously. Although a very common usage is for simultaneous activities, don’t overlook the “in any order” clause. You can use forks and joins and threads to show that all the activities can be performed in whatever order the user chooses, as in Figure 2-40.
Although this is technically correct, I find it makes matters worse, because forks and joins are a hard concept for nondevelopers. So to solve a simple problem, we introduce a hard notation and have to explain it every time it appears. Plus, I really do prefer to reserve forks and joins for simultaneous activities.
Another alternative is simply to go with the diagram that we had to start with, but this time we’ll add a note to explain, as in Figure 2-41. This is pretty clear, as long as no one misses the note.
But the best approach I believe is to use a note and a not-so-revolutionary technique called a screen shot. Really, is there any developer working in a modern GUI who will be confused by Figure 2-42?
As Ambler says in Agile Modeling, “The UML Is Not Sufficient.”14 There are things that UML expresses well; and there are things that are more easily expressed with other forms of diagrams, with pictures, or with words. I may disagree with Ambler in degree—I think UML may be applied in a lot of places where he thinks it’s insufficient—but I think he’s fundamentally correct. As always, our job is to communicate, not to be “correct.”
14. Scott W. Ambler, Agile Modeling: Effective Practices for eXtreme Programming and the Unified Process ( John Wiley & Sons, 2002), pp. 169–171
But What If the User Clicks Cancel?
There’s another problem with modeling the modern GUI: users can cancel what they’re doing in a wide variety of circumstances, often at any point along an Activity Diagram. (Curse those users! Always doing what they want, rather than what we tell them to do.) Just imagine trying to modify Figure 2-40 to add a branch at every point where the user might cancel the procedure. (And if that doesn’t scare you, imagine trying to modify Figure 2-38 or Figure 2-39.) Flexibility is hard to model.
So how would you model this flexible ability to cancel? You could simply train your people to expect cancellation at any time, regardless of whether it’s shown in the diagram or not, or make sure that you add a note to your diagrams that explicitly declares that the user may cancel.
Another option would be to add a subactivity state that contains all the activities that may be cancelled. Unlike my earlier advice on subactivity states, when I use a subactivity state for common event handling, I like to show the subactivity state and its constituent activities on the same diagram, as in Figure 2-43.
Figure 2-43. Component Activity Diagram for the Get Owner Name use case, with CancelThis picture may seem a bit like overkill, so you might want to stick with Figure 2-41 and add another note. But this diagram does convey that the user can’t cancel during the Get or Add Owner activity.
A Note on Notes
UML isn’t perfect. Nothing is, and the creators of UML knew better than to try for perfection. That’s why they included stereotypes as an extension mechanism. (See Chapter 3 for more on stereotypes.) And that’s also why they included notes. A note is a block of text that appears in a diagram and describes some aspect or detail of the diagram that might be unclear, or that might be difficult to convey pictorially.A note appears in an icon that looks like a page with a corner turned down. It may also be attached with a dashed line to the diagram elements it describes. You should be sure to add notes wherever they provide useful clarification to a diagram. But don’t get carried away: if you have a three-page note with three little diagram icons in the lower-left corner, you’re writing a functional spec, not building a UML model; and most UML tools make lousy word processors.
blog comments powered by Disqus