Five-Step UML: OOAD for Short Attention Spans – Design, Repeat

This article continues our introduction to the concepts of Five-Step UML, working from beginning to end. It introduces UML notation and goes into great detail. This article covers the final two steps of a five-step process. It is from chapter 2 of UML Applied A .NET Perspective, written by Martin L. Shoemaker (Apress, 2004; ISBN: 1590590872).

Step 4: Design

Show the relations among the elements with Component Diagrams. Add other diagrams where they shed light on the components.

In this step, you’ll assign the interfaces and user interfaces discovered in Step 3 to components, and then show the relationships between components and interfaces in Component Diagrams. You’ll try to identify existing components and interfaces that can be reused, along with new components that will support the new interfaces.

UML Notation

A Component Diagram is a way of depicting the executables and other components that form the logical architecture of your system. At its core, a simple Component Diagram consists of four elements: components, interfaces, realizations, and dependencies. Each of these elements is described in the following sections.

Components

A component depicts a deployable part of your system: an executable, a library, a data store, etc. It’s a physical representation of a group of related classes and objects within your design.

In a Component Diagram, a component icon appears as a rectangle with two rectangular “attachment points” (which are purely decorative; nothing actually attaches to them).

Components: A .NET Perspective

In a .NET solution, a component is any chunk of code or data that you deploy and maintain. Some examples include

  • An EXE that represents a console app, a WinForms app, or a Windows service

  • A DLL that represents a code library, a WinForms custom control library, or an ASP.NET control library

  • The ASPX files that define the Web pages in an ASP.NET site

Some UML practices and tools go even farther, modeling components for the source files that make up your solution as well.

Interfaces

An interface represents one or more related services realized (i.e., provided or implemented) by a component. One component may realize multiple interfaces; and multiple components may realize a given interface.

In a Component Diagram, an interface icon appears as a circle. An interface should also include documentation of its attributes and operations. These might be depicted in a Class Diagram, using the standard class notation with the UML «interface» stereotype; or you might simply list them.

Interfaces: A .NET Perspective

It is important to remember that UML is conceptual, not technological: it isn’t tied to a particular technology; rather, its concepts can be used to represent a range of technologies. When people name things, unfortunately, they have a tendency to reuse and overload terms; and thus, the names are potentially confusing when different speakers come from different backgrounds.

So from a UML perspective, any mechanism that two components use to communicate is an interface: socket messages, HTTP messages, .NET Remoting calls, SQL calls, etc. But in .NET, “interface” has a very specific meaning: a precise protocol by which you can specify a set of operations and attributes that may be used by multiple client classes and provided by multiple server classes. A .NET interface may certainly be modeled as an interface in UML; but not every UML interface is a .NET interface. (You may find it useful to use stereotypes to distinguish various kinds of interfaces.)

Realizations

A realization indicates that a component realizes a given interface. It’s indicated via a solid line connecting the component to the interface.

Dependencies

In general, a dependency in your model indicates that one element makes use of or depends upon another; and thus changes in the source element will necessitate changes in the dependent element.

In Component Diagrams specifically, dependencies are drawn from components to the interfaces they make use of. A dependency appears as a dashed arrow.

Component Diagrams

Given these elements, then, a Component Diagram depicts components, interfaces realized by them, and dependencies from components to interfaces. Figure 2-18 is a Component Diagram that depicts the interfaces and components discovered in the Kennel Management System so far.


Figure 2-18.  Component Diagram for the Kennel Management System

So how do you arrive at this diagram? I’ll discuss that shortly, in the section “Step 4: Process in Detail,” where you’ll also see how to extend the diagram to more completely reflect your architecture; but first let’s take a quick look at .NET components.

Components: A (Further) .NET Perspective

Now the .NET developers have to make some specific technology choices, based on the interface technology choices from Step 3. ASP.NET pages and Web services have to be assigned to URLs and deployed to one or more Web sites. Each WinForm needs to be assigned to a component. Does one component provide all forms, or do you have forms distributed among multiple components? If you’re using interfaces or .NET Remoting, which components provide these services? If you’re using ADO.NET, which databases and tables and queries and views will you access? These and many more questions are part of designing a .NET architecture.

Exercise 204: Assign Interfaces to Components with Component Diagrams:

Build Component Diagrams based on the work you’ve already completed in previous exercises. Work with your team to pick out the interfaces from your swimlaned Activity Diagrams from the last step, define components, and create dependencies between them. The next section explains the process in detail.

{mospagebreak title=Step 4: Process in Detail}

Your starting point for this step is—naturally enough—the swimlanes that we created in the last step.

Pick an Interface

Your swimlanes from Step 3 reflect actors, interfaces, and user interfaces, so you need to select one of these as a starting point. Put an icon for it in a Component Diagram. Also, you should put the interface on a Class Diagram as a class icon with an «interface» stereotype, or you could just list it in a document. In either case, go through your swimlaned Activity Diagrams and find each place where the interface appears. Find each activity in the swimlane that is called from another swimlane, and add that activity to the interface as an operation. (If the activity has been converted so that it is now part of a subactivity state and is no longer called from outside, then you should only list the subactivity state as an operation, not its constituent activity states.)

Assign the Interface to a Component

Brainstorm and describe the component that might realize the interface. Perhaps you have a preexisting interface provided by a known, existing component. Here are some common preexisting interfaces:

  • ODBC (Open Database Connectivity). A common SQL mechanism sup- ported by many databases. It might be realized by Oracle, Microsoft SQL Server, Microsoft Access, or a number of other SQL databases.

  • ADO.NET. The .NET answer to common database access.

  • MAPI (Messaging API). A common mechanism for accessing mail services. It might be realized by Microsoft Outlook or a number of other mail servers.

  • HTTP (Hypertext Transfer Protocol). A common mechanism for document transfer on the Internet. It might be realized by Microsoft Internet Infor- mation Server (IIS), the Apache Web Server, or other Web servers.

  • FTP (File Transfer Protocol). A common mechanism for file transfer on the Internet. It might be realized by Microsoft Internet Information Server (IIS) or other file servers.

  • SQL (Structured Query Language). A common mechanism for storing and querying data. It might be realized by Microsoft SQL Server, Oracle DBMS, or other database servers.

Or perhaps you have already defined a new component, and believe it should provide a newly identified interface. Or perhaps your new interface will require a freshly discovered new component.

If the component (preexisting or new) isn’t already on your Component Diagram, add it as a component icon. Then connect the component to the interface via a realization line.

Repeat for Each Interface

Add all additional interfaces to the Component Diagram, and indicate which component or components realize each interface.

From the Activity Diagrams, Identify Dependencies

For each interface, examine its corresponding swimlanes in the Activity Diagrams. Look at the other swimlanes that have transitions into those swimlanes. Those transitions define dependencies: the component that realizes a “calling” swimlane is dependent upon the interface for the “called” swimlane. Add the dependencies indicated. (See the later example for more details on how to do this.)

Rearrange to Make a Legible Diagram

Consider ways to make the Component Diagram more legible:

  • Try to avoid crossing lines whenever possible.

  • Consider whether you may need to break the Component Diagram into multiple diagrams to show various interactions.

  • Hide extraneous details in the diagrams.

Consider also that some rearrangements are worse than others. Save your work, and be ready to undo and try other approaches. Aesthetics and readability can involve a lot of trial and error.

Repeat and rearrange until you’re comfortable that you have an architecture that can fulfill the scenarios as depicted in the Activity Diagrams.

That all sounds straightforward enough, doesn’t it? Still, there’s a lot going on here, so let’s see how this all works in an example.

{mospagebreak title=Example}

Let’s start of with an interface for the Owner Info swimlane (which appeared on many of the diagrams we examined in Step 3).

Owner Info Interface

We’ll put a corresponding interface on the (currently trivial) Component Diagram, depicted in Figure 2-19.

 

Figure 2-19. Initial Component Diagram for the Kennel Management System

At the same time, we’ll add the interface to a Class Diagram. Then, looking at the swimlaned Activity Diagrams shown earlier in this chapter, we can pick out the operations of Owner Info. You’ll find that we need two operations: Get or Add Owner and Validate ID. (Some other possible operations are Look Up Owner and Add Owner Record; but because these have been converted to activities inside Get or Add Owner and are no longer called from other swimlanes, we can skip these as operations. We could choose to make them operations, giving us flexibility in the future; but so far, nothing in our architecture requires them to be operations.) So our initial Class Diagram for our interfaces looks like Figure 2-20.


Figure 2-20.  Initial interface Class Diagram for the Kennel Management System

Kennel Management Engine Component

Let’s assume that the Owner Info interface will be provided by the Kennel Management Engine, which we’ve just defined. The Kennel Management Engine shall provide a wide range of data management and validation services. It is essentially the middle tier between presentation and storage. We’ll add this as a component to our Component Diagram, which should now look like Figure 2-21.


Figure 2-21.  Revised Component Diagram for the Kennel Management System

 

Other interfaces we can define from our swimlanes (in the earlier Activity Diagrams) are Pet Info and Vital Stats Info. Adding these, our interface Class Diagram looks like Figure 2-22.


Figure 2-22.  Revised interface Class Diagram for the Kennel Management System

Both of the new services really belong in our middle tier, and thus belong to the Kennel Management Engine. So our revised Component Diagram looks like Figure 2-23.


Figure 2-23.  Component Diagram for the Kennel Management System, second revision 

Hmmm . . . not very informative yet, is it? Well, it is, a little: it tells us three sets of things that we can ask the Kennel Management Engine to do. But it will tell us a lot more when we add dependencies.

When we look at our swimlaned Activity Diagrams, however, we see that almost all of our “calls” to our interfaces come from user interface swimlanes, not interface swimlanes. The only exception, as shown in Figure 2-13, is that the Pet Info swimlane has a transition to the Vital Stats Info swimlane. The provider of the Pet Info interface is the Kennel Management Engine; so this implies that the Kennel Management Engine is dependent on the Vital Stats Info interface, as shown in Figure 2-24.


Figure 2-24.  Component Diagram for the Kennel Management System, third revision

Although this is legal UML, is it meaningful? Perhaps.

Perhaps we believe that components yet to be defined will need the Vital Stats Info interface, so we need it in our architecture; and then Figure 2-23 would indicate both that the interface exists and that the Kennel Management Engine accesses vital stats through the interface, not through its internal mechanisms. (We might choose this approach so that we may change the architecture to add a separate vital stats component without having to significantly rewrite the Kennel Management Engine.)

On the other hand, we could decide that vital stats will only ever be accessed by the Kennel Management Engine. In that case, we can “collapse” the Vital Stats Info interface, demoting it to a mere class within the Kennel Management Engine. Don’t be afraid to collapse interfaces: discovering superfluous interfaces is a natural part of an evolving architecture. Similarly, don’t be afraid to preserve a seemingly superfluous interface: modularizing and exposing your “internal” services gives you a wide range of architectural choices down the road. You have to select your own balance between simplicity and flexibility; and whichever choice you make, you can always change your mind later. It just may entail some work— maybe a lot of work . . .

In this example, we’ll collapse the interface to yield the diagram shown in Figure 2-25.


Figure 2-25.  Component Diagram for the Kennel Management System, fourth revision

OK, so we’ve resolved the vital stats issue; but in the process, we’re back to a pretty simplistic Component Diagram. Looking at our Activity Diagrams, we can see some dependencies that are pretty clearly implied: our user interface swim-lanes have transitions to our interface swimlanes, so some sort of user interface components must lie behind those swimlanes. We just need to define them. Looking at the swimlanes, it appears we need some sort of Check In Form and some sort of Check Out Form. We can add these to the Component Diagram as components, add the dependencies implied by the Activity Diagrams, and voilà! We have the Component Diagram shown earlier in Figure 2-18.

{mospagebreak title=Working with User Interfaces}

Did that last step bother you? It bothers me: some of the swimlanes correspond to interfaces, but others correspond to actual components. Now this is legal UML. (In fact, it’s irrelevant UML: UML doesn’t require any mapping from interfaces or components to swimlanes, though the mapping is allowed. We use the mapping here as a useful convention of the Five-Step UML process.) But it’s inconsistent UML.

That bugs me. I had a nice, linear process: look at a swimlane; make it into an interface; assign it to a component; look at a component and its swimlanes; draw the dependencies. Now there’s a bump in my process.

In fact, there are two bumps: we also have swimlanes that correspond to users of the system, and those don’t show up on the Component Diagram at all. Even worse, those end users are represented in the model as actors; and so are other computer systems with which our system must interact. Yet we interact with those systems via interfaces, which do appear on the Component Diagrams. So we have two kinds of actors and two kinds of swimlanes that are similar in nature (within our model, anyway); but one is represented on the Component Diagram, and one isn’t. Traditional architecture diagrams don’t actually represent the end user; but if architecture is about “ . . .the selection of structural elements and the interfaces through which they are connected, the large-scale organization of structural elements and the topology of their connection, [and] their behavior as specified in the collaborations among their elements . . .”,9 then I believe it is a mistake not to include the end user in relevant architectural diagrams. Users are “connected” to the system through user interfaces; the topology of elements is shaped in part by the users with which it must be “connected”; and users collaborate with the system to do work (witness the fact that actors can appear as collaborators in Sequence Diagrams).

We can smooth out both of these bumps by adopting a new stereotype, «user interface», which may be applied to interfaces. The attributes of a «user interface» element represent the data fields displayed by the UI; and the operations of a «user interface» element represent the actions a user may take through the UI. Besides defining this stereotype, I find it useful to define a custom icon that looks somewhat like a computer monitor. Its square shape makes it easy to distinguish from regular interfaces.

Then we can add «user interface» elements to the Component Diagram the same way we added regular interfaces: look at a swimlane that represents a user interface, and add the corresponding «user interface» element. Then document the «user interface» element in a Class Diagram to depict its data fields and actions.

So, for our example, the user interfaces we have defined so far could be modeled in a Class Diagram as shown in Figure 2-26.


Figure 2-26.  User Interfaces for the Kennel Management System

9. James Rumbaugh, Ivar Jacobson, and Grady Booch. The Unified Modeling Language Reference Manual (Addison-Wesley, 1999), p. 151

And then we can select or define a user interface component to realize the «user interface» element. Following this procedure (and ignoring that whole vital stats controversy—what was I thinking when I put that in the architecture?), the diagram in Figure 2-23 would now look like the one in Figure 2-27.


Figure 2-27.  Component Diagram for the Kennel Management System, fifth revision

Note how we assign one component for each user interface. This is a common practice for modeling ASPX pages: each page is a component in its own right. In this sense, we can think of the user interface as the HTML delivered to the user’s browser, and the component as the code-behind component that creates the HTML and processes the response. Although this approach can be useful, it can also be cumbersome, as you’ll see in Chapter 9.

Note also how, by adding «user interface» elements and corresponding components, we pick up something we had missed in our architecture earlier: the Paging System to notify the Care Giver when the pet needs to be retrieved. This is certainly an improvement in our architecture; but it also reveals another problem. In Figure 2-17, we have one user interface swimlane (Check Out UI) calling another (Care Giver UI). Now this is legal, because a form can certainly launch another form; but it’s clearly not what we meant: we wanted an interprocess communication that would cause the Care Giver UI to notify the Care Giver. Interprocess communication implies a new interface, one we didn’t capture correctly in Figure 2-17. We’ll call it Care Giver Notify, and add the corresponding swimlane to the diagram in Figure 2-17, producing the one shown in Figure 2-28.


Figure 2-28.  Activity Diagram for Locate Pet use case (architectural view, revised)

Note the use here of a new stereotype, «display», applied to a transition to a user interface to indicate that it must display information for the user’s response. From this diagram, we can add the Care Giver Notify interface to the diagram in Figure 2-27, producing the one in Figure 2-29.


Figure 2-29.  Component Diagram for the Kennel Management System, sixth revision

Next we can add dependencies the same way we did before. Recall that in order to add dependencies, we examine each swimlane, and look at the swimlanes that have transitions into it, converting those into dependencies. By following this procedure and rearranging for legibility, we get the diagram in Figure 2-30.


Figure 2-30.  Component Diagram for the Kennel Management System eighth revision

Now that we’ve gone this far, we might as well go all the way: adding the actors corresponding to swimlanes, and adding dependencies from the actors to the user interfaces they use. This will give us a complete picture of the roles that users play within the architecture, as shown in Figure 2-31.


Figure 2-31.  Component Diagram for the Kennel Management System, eighth revision

If you would rather stick with formally correct UML notation, you could define a new stereotype for components: «user». You could then add a new component with that stereotype wherever you want to show a user interacting with a user interface. But even though it’s not standard UML, I find it easier and clearer to just put the actors on the Component Diagrams, as shown above. And again, if it helps you to communicate, it’s useful UML, even if it’s not perfect.


So What About Those Names?

Here in Step 4, we’re stepping out of analysis and into design (architectural design, to be precise). That means the programmers in the crowd are starting to think about implementation (because we programmers have a nasty habit of running ahead of where we’re supposed to be). And that means that some of you reading this right now are saying, “Hey, those names aren’t legal identifiers in my implementation language. What’s up with that?” And you’re right, of course: Kernel Management Engine, Owner Info, and Get or Add Owner aren’t legal identifiers in any programming language I know. And if you start talking corporate naming standards and coding conventions, the situation only gets worse.

So what is up with that? Well, in my practice, I usually try to share my architecture with my stakeholders as it evolves, so that they can give me an outside perspective. (OK, I really only share selected, simplified views. I don’t want to bury them in technical details.) So I prefer human-readable names. andIfYouThinkThisIsHumanReadable, youHaveBeenProgrammingTooLong, and youNeedToGetOutIntoTheRealWorld. So how do I deal with this disconnection between readable names in my model and legal identifiers in my code?

I have three suggestions:

  1. If you’re not generating code from your model automatically, you have to translate your model into code by hand. While you’re doing so, translate the names into legal identifiers.

  2. If you are generating code from your model automatically, most modeling tools will automatically translate the names into legal identifiers. Some will even let you define the translation rules, such as “Convert all spaces to underscores” or just “Remove all spaces.” You can even specify common conventions such as “All class names begin with ‘C’” and “All interface names begin with ‘I’.”

  3. If you’re not worried about communicating with nonprogrammers (and why not, eh?), follow your naming rules and conventions in the model.

Of these approaches, I personally prefer the second choice.

 


TIP: To learn more about Component Diagrams and architecture, see Chapter 8.

{mospagebreak title=Step 5: Repeat}

Narrow the scope of your process to individual elements (designed with Class Diagrams). Add other diagrams where they help you understand the system. Repeat Steps 1 through 4 as appropriate for the current scope.

In this step, you’ll narrow your scope to look at a single component (as identified in Step 4). By zooming in to the scope of the component, you can apply the same analysis and design processes to determine the structure of the component.

You’ll begin by playing “Interface Hangman”: treating the interfaces to a component as actors from the perspective of the component, as shown in Figure 2-32.


Figure 2-32.  Interface Hangman

If you’re confused about this step, consider this definition of an actor:

An actor represents any outside entity that interacts with your system. It may request services from your system; and it may perform services for your system.

Well, in this step, you narrow your scope so that the only “system” of interest is one particular component (at a time). And “outside” that component lie any actors that interact with it and any other components that interact with it. These all meet the definition of “actors” now that you’ve narrowed your scope; however, one rule for interfaces is that they define everything that is known about the connections between components, both for the source component and for the client component. Thus, from the perspective of the current component, the interfaces and user interfaces that it realizes are all that you know about the actors that make requests of it; and the interfaces on which it depends are all that you know about the actors that provide services to it. So it will have actors that represent the users of any user interfaces it provides; but you’ll also create “component actors” that represent the interfaces related to the current component. (Despite Figure 2-32—which is drawn only to convey the idea of interfaces as actors, not as an example you should follow—you may want to use interface icons to represent these component actors, rather than actor icons. This will emphasize that these are interfaces, not people.)

If the interfaces are component actors, then the methods of interfaces realized by the current component may be treated as component use cases. Again, consider this definition of a use case:

A use case represents what your system does in response to a communication from an actor, and represents how your system carries out a requirement of that actor. It appears in a diagram as a simple descriptive phrase (an action, not an object); but within your model, it’s a placeholder to which you’ll attach additional documentation, more detailed diagrams, and anything you learn about the required behavior.

So if a use case represents behavior required by an actor, then a component actor’s requirements—and thus its component use cases—are defined by the operations of the interface it represents. No other requirements are possible, because the interface completely defines how the component and its client may interact. The only other requirements are those of the end user actors who make use of the component’s user interfaces.

So in this step of Five-Step UML, you’ll perform the following substeps:

  • Define the component’s behavior with component Use Case Diagrams, one or more for each interface of the current component, and one or more for each user interface of the current component.

  • Refine the behavior by producing a component Activity Diagram for each component use case.

  • Assign the activities in the component Activity Diagrams to classes, using swimlanes.

  • Design your internal architecture of the component with Class Diagrams (described in the next section).


UML Notation

The only particularly new elements in this step are those related to Class Diagrams: classes, associations, and dependencies.

Classes

A class represents the operations and attributes of one or more objects within your system. It binds attributes and operations to completely define the behavior of the objects. Thus a class definition serves the same purpose for an object that an interface definition serves for a component: it describes the ways in which client elements may use the given element.

In a Class Diagram, a class appears as a rectangle broken into three sections. The top section identifies the name of the class, the middle lists the attributes of the class, and the bottom section lists the operations of the class.

If it makes the diagram less cluttered and thus more clear, you may hide the attributes or operations for any class in the diagram. You may even hide some of the attributes and operations, while showing others. But I usually discourage this—unless the class definition is really large and overwhelms the rest of the diagram—because readers tend to assume that a partial list is a full list.

Classes: A.NET Perspective

Now you’re moving from domain classes to code classes. You need to consider the precise .NET mechanisms for implementing each class. What is its base class? What are its attributes, including types and initial values? What are its operations, including parameters and return types? What kind of class is it: a class, a structure, an enumeration, a delegate?

Associations

An association represents an object of one class making use of an object of another class. It is indicated simply by a solid line connecting the two class icons. An association indicates a persistent, identifiable connection between two classes. If class A is associated with class B, that means that given an object of class A, you can always find an object of class B, or you can find that no B object has been assigned to the association yet. But in either case, there is always an identifiable path from A to B. Class A uses the services of class B or vice versa.

Associations: A .NET Perspective

In .NET code, an association is most probably implemented as one class containing another—or to be more precise, containing a reference to the other, since all .NET classes other than structures are always contained by reference. For some designs, each class might contain a reference to the other. These concepts are discussed further in Chapter 4.

Dependence

In Class Diagrams, a dependence represents an object of one class making use of or somehow “knowing about” an object of another class; but unlike association, dependence is a transitory relationship. If class X is dependent on class Y, then there is no particular Y object associated with an X object; but if the X object “finds” a Y object—perhaps it is passed as a parameter, or it receives one as the return from an operation that it calls, or it accesses some globally accessible Y object, or it creates one when it needs one—then it knows what it can do with the Y object. Object X is potentially affected by a change in Object Y.

As in other diagrams, dependence is indicated by a dashed arrow connecting the two class icons.

Dependence: A. NET Perspective

In .NET code, dependence has become almost a nonentity. In old C++ code, for example, dependence could be implemented as one class #include’ing the header file for another class. That #include statement indicated that the first class knew what to do with the second class. But in .NET, most classes are visible to other classes. The closest things to dependence in .NET are

  • The using directive, indicating that all code in a file can use all classes in a particular namespace

  • Assembly references, indicating that a project can use all classes in a particular assembly

But both of these uses are package specific, or perhaps component specific. You may choose to avoid dependence for this reason; but I still prefer to model dependence among classes, because it indicates that one class may create or otherwise manipulate objects of another class.

Class Diagrams

Given these elements, then, a Class Diagram depicts classes and associations between them. Figure 2-33 is a Class Diagram that depicts the classes and associations that may be useful in the Kennel Management System.


Figure 2-33.  Class Diagram for the Kennel Management System

Classes: A (Further) .NET Perspective

The .NET Framework contains over 3,300 classes for common infrastructure operations. Before you design your own classes, you might save time to see if .NET gives you classes that provide the functionality you need, or at least a large chunk of it.


TIP: To learn more about Class Diagrams and design, see Chapters 4 and 9.

Exercise 205: Define, Refine, Assign, and Design Within Your Components:
Pick a component to design. (You’ll design all of them eventually.) Then walk through the following procedures to build a design model for that specific component.

{mospagebreak title=Step 5: Process in Detail}

In this step, you simply take each component identified in the last step, and repeat the whole process from beginning to end, as if the component were a system in its own right. For each interface of the component, you break out the detail into Activity Diagrams and then, by adding swimlanes, identify the classes that are responsible for each action. By examining how calls are made between swimlanes, you can work out the associations and dependencies between classes.

Let’s see how this process works in more detail.

Define

Collect together all of your components by adding a component to your model for each interface to your component. Treat your component like an actor and repeat the requirements definition process, as if each method of an interface were a use case for the (component) actor. If an earlier diagram indicates that a method uses another interface from some component, add that other interface as a collaborating actor, and label the communication to that actor with the name of the requested method.

If you’re working through this process as an exercise, then I recommend that you do at least two component Use Case Diagrams for practice, and more if you wish.

You should also think about user interfaces at this stage. If your component provides any user interfaces, add the actors that use those interfaces to your model; and add the actions supported by that user interface as use cases for that actor. If an earlier diagram indicates that an action uses another interface from some component, add that other interface as a collaborating actor, and label the communication to that actor with the name of the requested method.

Refine

Take each component use case, and repeat the requirements refinement process by creating a thorough component Activity Diagram for the use case. (If you’re working through this as an exercise, do at least two component Activity Diagrams for practice, and more if you wish.) Define your Primary Scenario for each Activity Diagram, and then define your Exception Scenarios.

Recall that during analysis, you were supposed to concentrate on business rule exceptions (e.g., “What happens if the user isn’t authorized to use the system?”). Now is the time to concentrate on implementation exceptions (e.g., “What happens if the hard drive fills up?”). These component Activity Diagrams aid you in preplanning how to detect and handle exceptions.

Note that, if you used subactivity states in Step 2 or Step 3 to simplify your requirements Activity Diagrams, you may have given yourself a head start in designing your component Activity Diagrams. If a subactivity state represents one method of an interface or one action of a user interface, then its constituent activities capture some important business rules that must be implemented.

Finally, examine your Activity Diagrams to see whether subactivity states might simplify any complex logic.

Assign

Add swimlanes to each component Activity Diagram. Within a component Activity Diagram, the swimlanes will represent component actors (either true actors or interfaces related to the class) and objects of classes. As you add swim-lanes for new objects, add the corresponding class icons on a separate Class Diagram. As you assign activities to each object, make them into operations documented in the class icon.

It is common to have some sort of “gateway class” that serves as the entry to the component for the interface. For a user interface, the gateway class will be the form or dialog box that provides the user input. For a component interface, the gateway class will be a class that realizes the interface, providing the same attributes and operations as the interface (plus others as needed). The gateway provides the surface implementation of the responsibilities specified in the interface. (This isn’t the only way to implement an interface, but it is a very natural approach in any OO language.) You depict a class realizing an interface much as you show a component realizing an interface: a circle for the interface, connected to the class via a solid line.

Design

Now revise the component Class Diagram to depict the associations required by the component Activity Diagrams. Your swimlanes in this step reflect interfaces, user interfaces, and classes. Select one of the interfaces from the swimlanes, and put an icon for it in a Class Diagram. Then, if the interface is realized by one of your classes, add that class, with the realization line connecting them. (If the interface is external to your component, just show the interface.) Repeat for each interface swimlane. (Don’t add «user interface» elements, because they’re entirely described by the classes that provide them.)

Next add classes for the remaining swimlanes. Also add classes for the object flow states in your Activity Diagrams. Think about what attributes each class will need to support its operations. Add these to its class icon.

Needless to say, all of this takes a lot longer to do than it does to say. Let’s see how all this works out in an example.

Example

As part of our example in Step 4, we saw that the Kennel Management Engine component will realize two interfaces: Pet Info (the interface for manipulating Pet information) and Owner Info (the interface for manipulating Pet Owner information). Pet Info and Owner Info both require simple edit and query methods.

Pet Info and Owner Info Interfaces

For the Pet Info interface, this results in the component Use Case Diagram shown in Figure 2-34.


Figure 2-34.  Component Use Case Diagram for the Pet Info interface

Note the communication from the Write Vital Stats use case to the Vital Stats Info interface, with a Create Vital Stats Record label. This corresponds to the information in our earlier swimlaned Activity Diagram shown in Figure 2-13, and tells the designers and implementers of Write Vital Stats how they are expected to carry out this responsibility.

Now, I know I told you to design one component at a time (on a real project, you might have one team designing each component simultaneously); but for the sake of this example, I’d like to turn to another component, one that has a user interface. We’ll choose Check In Form because it’s most interesting. Because they provide complementary examples, we’ll look at both Kennel Management Engine and Check In Form for the rest of this example.

So looking at Check In Form, what was the next step again? Ah, yes:

If your component provides any user interfaces, add the actors that use those interfaces to your model; and add the actions supported by that user interface as use cases for that actor. If an earlier diagram indicates that an action uses another interface from some component, add that other interface as a collaborating actor, and label the communication to that actor with the name of the requested method.

Well, by examining the Check In Form component that we diagrammed in the last step, we can see that it provides a user interface, Check In UI; and that it has three actions, as indicated in Figure 2-26: Get Owner Name, Get Pet Name, and Get Vital Stats. These become component use cases for Check In Form; and the actor interacting with those use cases is the Pet Owner, as indicated by Figure 2-30. Combining this information, we end up with Figure 2-35.


Figure 2-35.  Component Use Case Diagram for the Check in UI

Note how, based on our swimlaned Activity Diagram for the Get Pet Info use case shown in Figure 2-12, we add one communication to the Owner Info interface and two to the Pet Info interface.

{mospagebreak title=Creating Activity Diagrams}

Figure 2-36 shows the Activity Diagrams for Get or Add Owner, with logic added to handle implementation exceptions.


Figure 2-36.  Component Activity Diagram for the Get or Add Owner use case

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.


Figure 2-37.  Component Activity Diagram for the Get or Add Owner use case, revised

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.

—Steve McConnell13

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.

  1. Karl E. Wiegers, Software Requirements, Second Edition (Microsoft Press, 2003), p. 35

  2. Watts S. Humphrey, Managing the Software Process (Addison-Wesley, 1989), p. 25

  3. Ivar Jacobson, Grady Booch, James Rumbaugh, The Unified Software Development Process (Addison-Wesley, 1999), p. 104

  4. Steve McConnell, Rapid Development: Taming Wild Software Schedules (Microsoft Press, 1996), p. 331

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.


Figure 2-38.  Component Activity Diagram for the Get Owner Name use case

 

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.


Figure 2-39.  Component Activity Diagram for the Get Owner Name use case: a redundant revision

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.


Figure 2-40.  Component Activity Diagram for the Get Owner Name use case a confusing revision

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.


Figure 2-41.  Component Activity Diagram for the Get Owner Name use case: a not-too-bad revision 

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?


Figure 2-42.  Screen shot for the Get Owner Name use case

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 Cancel

This 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.

 

{mospagebreak title=Adding Swimlanes}

Adding swimlanes to our Component Activity Diagram for the Get or Add Owner use case (Figure 2-37), we get the Activity Diagram shown in Figure 2-44, where the Owner Info interface is realized in a gateway class, COwnerInfo, which carries out the assigned method.

Figure 2-44.  Activity Diagram for the Get or Add Owner use case, with swimlanes

Usually, a gateway class relies upon deeper classes and interfaces to carry out its responsibilities; here, that’s COwnerSet, which does the work of calling into the SQL database.

Although we could have had COwnerInfo call directly into a SQL database to work with owners, that’s not the wisest implementation. I like to isolate the SQL handling inside wrapper classes, because SQL itself isn’t inherently object-oriented. Mixing SQL’s relational behavior into the design may make matters more confusing.

Furthermore, I usually have a wrapper class that represents a table or result set, and another class that represents one record in the set. The set class supports lookup, creation, and deletion. The record class supports editing and (perhaps) deleting. Both classes make use of a SQL interface to the database.

Note how the SQL interface wasn’t previously reflected in our architecture. Depending on our organization rules, this might be a decision that a component team can make on its own, because other teams don’t need to know how the team implements its responsibilities; or it might require the architecture team to add the interface, select a SQL engine, and update the architecture, as shown in Figure 2-45.


Figure 2-45.  Component Diagram for the Kennel Management System, ninth revision

Again, the real design process is sloppy and iterative, with a lot of feedback and retracing of steps. Just as architecture influenced requirements, design will influence architecture.


SQL Code or Stored Procedures?

Notice the two activities assigned to the SQL interface: Select Owner and Insert Owner. These might correspond to actual SQL Select and Insert and Update statements; or you might make SQL stored procedures to carry out those tasks.

A stored procedure is a canned set of SQL statements—Select, Insert, Update,

and a number of other commands—that may be called with parameters and

that look from outside the database as a single SQL call. (Depending on your

database engine, a stored procedure may also be compiled and optimized for

better performance.)

I prefer to consider these activities as stored procedures (it lets me think of SQL

more like an interface to a service than like a complex language), but this diagram

doesn’t actually reflect that. So to make my SQL intentions clearer, I like to use

two new custom stereotypes for transitions: «SQL» for hard-coded SQL calls,

and «stored proc» for SQL stored procedure calls. So the diagram in Figure 2-44

can be revised as shown in Figure 2-46.


Figure 2-46.  Activity Diagram for the Get or Add Owner use case, with swimlane, second revision

…………………………………………………………………………………

{mospagebreak title=Object Flow States}

Earlier, I said that I preferred a separate class to represent individual records in the set of items from the database; but that class doesn’t appear in Figure 2-44 or Figure 2-46. Why is that?

Well, I could have added a separate swimlane to represent that class ( COwnerRecord); and then I could have shown transitions from COwnerSet to that swimlane to indicate when a record object was created. That would be entirely legal UML; but would it communicate? I don’t think so: in the context of this diagram, that swimlane would have no other responsibilities. So it would take up space and add detail without really adding information.

Instead, I prefer to use a more advanced part of the Activity Diagram notation: object flow states. These are icons you add to the Activity Diagram, not to represent activities the system will perform, but simply to represent objects that are manipulated by the activities. An object flow state (or more simply, an object) appears as a rectangle with the name and the class of the object, separated by a colon, both underlined. (If you haven’t selected both a name and a class yet, just list the one you have selected.) You can show an activity creating or modifying an object by a dashed arrow from the activity to the object; and you can show an activity reading information from an object by a dashed arrow from the object to the activity.

For example, we could add the COwnerRecord to the diagram in Figure 2-46 as shown in Figure 2-47.


Figure 2-47.  Activity Diagram for the Get or Add Owner use case, with swimlanes and object flow state

There are three situations in which I find object flow states to be particularly useful. One is when I need to demonstrate creating an object that’s a return value, as in Figure 2-47. I find that it helps to see where the return object comes from.


Figure 2-48.  Activity Diagram for a loop with object flow state

 

The second is when I want to show some sort of cumulative results from within a loop, which are then used by an activity outside the loop. The object flow state can represent the cumulative results, as shown in Figure 2-48.

The third situation in which I find object flow states to be particularly useful is when I want to show an object through which two threads communicate and share information. This object can then hold any synchronization mechanism used to prevent contention between the two threads. If the two threads both write to the object (such as, perhaps, a logger object), then both threads would have outgoing transitions to the object, such as those shown in Figure 2-49.


Figure 2-49.  Activity Diagram for threads writing to a common object flow state

But if one thread is using the object to signal or control the other thread, then one transition should go in, and one should go out, as shown in Figure 2-50.


Figure 2-50.  Activity Diagram for a threads communicating via an object flow state

A common practice when working with object flow states in this way is described by the Three Amigos:

The control flow (solid) may be omitted when the object flow (dashed)

arrows supply a redundant constraint. 15

In other words, if an object flow state and a transition connect the same two activities, as shown in Figure 2-51, then you may omit the transition, as shown in Figure 2-52.


Figure 2-51.  Activity Diagram with an object flow state and a redundant transition


Figure 2-52.  Activity Diagram with an object flow state (redundant transition omitted)

15. James Rumbaugh, Ivar Jacobson, and Grady Booch, The Unified Modeling Language Reference Manual (Addison-Wesley, 1999), p. 139

This is legal UML, but I strongly discourage it. Now usually, I’m very flexible in the use of UML: you’ve seen that already in this chapter. So it surprises my students how strongly I object to this usage. But in my experience, this trains readers to think that dashed arrows always indicate control flow, not just data flow; and once they’re trained in that wrong habit, other diagrams can really confuse them. Look back at Figure 2-48: suddenly, Cumulative Result looks like some sort of “escape hatch,” allowing a premature exit from the loop. Even worse, look back at Figure 2-50: suddenly, Thread Signal looks like some sort of mechanism for breaking the wall between thread execution contexts, allowing the CPU to “jump the track” from one context to the other. Neither of those misperceptions is a correct reading of the diagram according to the rules of UML; but both misperceptions are encouraged when people are used to seeing the redundant transitions omitted. Thus, this habit leads to miscommunication, which is never our goal with UML.

Don’t go overboard with object flow states, because they may add excess detail to the diagram. Consider drawing the diagram with and without the objects, and then deciding which communicates better.

Creating Class Diagrams

Recall that our classes correspond to the swimlanes from the component Activity Diagrams. So for instance, look back to our latest version of the Activity Diagram for the Get or Add Owner use case (Figure 2-47). You can see our initial Class Diagram would look like the one in Figure 2-53.


Figure 2-53.  Initial Class Diagram for the Kennel Management Engine

Then from the Activity Diagrams, you need to identify associations and dependencies. For each class, examine its corresponding swimlanes in the Activity Diagrams. Look at the other swimlanes that have transitions into those swimlanes. Those transitions define either associations or dependencies. As discussed earlier, if the relation is persistent, draw it as an association (a solid line); but if the relation is transitory (lasting no longer than a single operation, as a rule), draw it as a dependence (a dashed arrow). You can also model return types via dependence. A good convention is to draw a class using an interface as a dependence, because an association (a solid line connecting the class to the interface) would look just like a realization (a solid line connecting the class to the interface).

Thinking about this example. It will make sense for COwnerInfo to always have a COwnerSet with which it is working, so we’ll add that as an association. COwnerSet uses the SQL interface, so that is a dependence; and because COwnerSet creates COwnerRecord objects as it needs them, we also model that as a dependence. Finally, because the Owner Info interface—and thus the COwnerInfo class—return a COwnerRecord, those should also be dependent on COwnerRecord. Thus, we end up with Figure 2-54.

In this case, however, I feel that the dependence of Owner Info on COwnerRecord just clutters the diagram with not a lot of benefit. I prefer the diagram in Figure 2-55.

Next we’ll look at the swimlane for each class, and convert its activities into operations of the class. Adding these to the class icon, we get the diagram shown in Figure 2-56.


Figure 2-54.  Class Diagram for the Kennel Management Engine


Figure 2-55.  Class Diagram for the Kennel Management Engine, revised


Figure 2-56.  Class Diagram for the Kennel Management Engine with operations

Note I didn’t add Look Up Owner nor Add Owner Record to COwnerInfo. Those represented logical steps within Get or Add Owner, not separate operations. I could even have hidden these within a Get or Add Owner subactivity state in Figure 2-47; but I felt that would complicate the picture, not improve it. Later, I might decide that neither Look Up Owner nor Add Owner Record are useful operations to have to simplify the code; but for now, I haven’t committed to that choice.

Finally, let’s add our attributes for these classes. Earlier examples tell us that a COwnerRecord needs First Name and Last Name attributes. We could depict these as shown in Figure 2-57.


Figure 2-57.  Class Diagram for the Kennel Management Engine, with attributes

Step 5(a): Repeat Again

Now narrow the scope of your process to individual classes. Repeat Steps 1 through 4 as appropriate for the current scope.

Once you have your Class Diagrams, it’s possible to go through Step 5 again, this time narrowing your scope to look at a single class. By zooming in to the scope of the class, you can apply similar analysis and design processes to elaborate the structure of the class.

So in this step, you’ll perform the following substeps:

  1. Define the class’s behavior by specifying the precise parameters and return values for each operation.

  2. Refine the behavior by producing a class Activity Diagram for each operation.

  1. Assign the activities in the class Activity Diagrams to attributes, associ- ations, temporary objects, and parameters, using swimlanes.

  2. Design your class in detail with Class Diagrams.

Now it may be that you find it easy enough to jump straight to code, skipping this step. Great! Do what works for you. But the same strategies that helped you to simplify complexity in requirements and in components may be useful in studying and designing complex classes.

From one of the Class Diagrams you’ve developed in the last exercise, pick a class to design. Then walk through the procedures described next to elaborate the design of that class.

{mospagebreak title=Step 5(a): Process in Detail}

Again, building on the work we did in the last step, we go through the same cycle again, this time focusing down to the level of the class. The following sections describe the process in detail.

Define

Look at each operation of your class, and determine its proper return value and parameters. Look at how the operation is used in earlier diagrams, and determine what information is provided to it and what is expected from it. Also consider whether any of its parameters should have default values.

You can add more detail to an operation in a Class Diagram by listing its parameters and return value. The parameters are listed inside the parentheses in a comma-separated list of the form param : type = default value, . . . . If a parameter has no default value, none is listed. Also, if there are any language-specific characteristics (such as .NET attributes that identify a parameter as in, out, or inout), these are listed in front of each parameter. The return type is listed after the parentheses, separated by a colon.

Refine

Take each class operation, and create a thorough class Activity Diagram for the use operation. (If you’re working through this process as an exercise, then you should do at least two class Activity Diagrams for practice, and more if you wish.) Define your Primary Scenario for each Activity Diagram, and then define your Exception Scenarios. As before, if you used subactivity states in the previous step to simplify your component Activity Diagrams, you may have given yourself a head start in designing your class Activity Diagrams.

Assign

Add swimlanes to each class Activity Diagram. Within a class Activity Diagram, the swimlanes will represent the class itself, along with its attributes, associations, and dependencies; so as you add swimlanes, you may discover that you need new attributes, associations, and dependencies. As you add swimlanes for these, update the class icons on related Class Diagrams. Swimlanes may also represent parameters to the method and return values, if these are required to carry out activities in the diagram. Once you’ve done this, then look for implementation operations required by the original Activity Diagram, and look for object flow states that may help explain how activities collaborate.

Design

Now revise your class definition to depict the new attributes, associations, and dependencies required by the class Activity Diagrams. Examine each swimlane (other than the swimlane for the class).

If a swimlane should be an association and the class doesn’t have that association yet, add it. Then, based on how the attribute is used, select an appropriate type for it, and perhaps a default value if one is appropriate. Update the class to reflect these decisions. The attribute type is listed after the attribute name, separated by a colon. If there is a default value, it is listed after the type, separated by an equals sign.

For each class, consider drawing a separate Class Diagram centered on the class, with associated and dependent and realized classes and interfaces arrayed around it. Sometimes this “halo” diagram, depicting a class and everything related to it, can be a useful overview for understanding the class. (Other times, it can be too trivial to be useful.)

Let’s see how this process works in practice.

Example

For our example, let’s look at the COwnerSet class. The Find Owner operation of the COwnerSet class needs the names of the owner to be found; and for simplicity, these names will be of type string. Create Owner needs the same information; and both operations may return a COwnerRecord. So COwnerSet looks like the class depicted in Figure 2-58.


Figure 2-58.  Class Diagram for COwnerSet

Figure 2-58. Class Diagram for COwnerSet

Next, we need to create the Activity Diagrams for these two operations, but here, we’ll just focus on Find Owner. Figure 2-59 is an Activity Diagram for the Find Owner operation.


Figure 2-59.  Activity Diagram for Find Owner

At this time, we can delve even further into implementation exceptions to aid us in preplanning how to detect and handle exceptions. In the same way that I used subactivity states to demonstrate common cancel handling (Figure 2-43), we could use subactivity states to depict common exception handling such as C#’s try/catch mechanism.

For example, in the diagram in Figure 2-59, there are two very likely exception categories. First, any time you call out to another component, exceptions are possible; and in particular, SQL operations are known to propagate exceptions. So we need to handle a range of possible SQL exceptions, which we can do by treating all possible SQL errors as a simple Null return. The second category of error is that we might not have enough memory to create an Owner Record. So, as shown in Figure 2-60, we’ll add exception handlers for these exceptions.


Figure 2-60.  Activity Diagram for Find Owner, with exception handling 

We might decide that this exception handling is too much detail for the design, and better left to code. Different teams will take different approaches to this technique.

Adding swimlanes and related activities to Figure 2-59, we get the Activity Diagram shown in Figure 2-61.


Figure 2-61.  Activity Diagram for Find Owner, with swimlanes

{mospagebreak title=Step 5(b): And Again?}

Some use case practitioners may take issue with the steps I’m recommending here. “Too detailed!” they’ll say. There are business analysts who employ use cases who will argue that the largest of organizations might be modeled with maybe 15 use cases. They will argue— strongly—that more than that is a sign that you’re thinking in too much detail. They will further tell you that you can’t “decompose” a use case into smaller pieces.

But then there are system designers who say, “I was working on a small system last month. It was only around 80 use cases.” (I’ve even heard both perspectives— a few large use cases versus many small use cases—coming from two different people within the same organization.)

So who’s right? Remember: if you ask me “ A or B?” my answer is usually “Yes” or “ C.” This is another “Yes” answer. I think business analysts are right: when trying to think of how to organize a business’s operations, you have to look at very high-level operations. If you have a large number of operations, you can lose track of everything. Their use cases are often along the lines of Accounting and Facilities and Human Resources.

But I think system designers who have more and more detailed use cases are right in that these use cases are more concrete and more implementable. If I ask ten programmers to implement a use case like Facilities, I’ll probably get 20 different implementations—or I’ll get ten programmers throwing things at me for giving them such a lousy spec. But if I ask ten programmers to implement Display Available Facilities, I’ll get a lot closer to consensus.

So it’s important that, at various stages of software development, you have use cases of varying size and scope, ranging from large and broad to small and detailed. I think that the same basic strategy embodied in Five-Step UML— define your system requirements in terms of the use cases needed by something outside your system; refine each use case into a set of activities; assign those activities to parts of your system; design the relationships among those parts; and repeat at the scope of a more detailed part of your system, in an iterative fashion—is a useful strategy across all the scopes of use cases. (It’s also neither new nor earth-shattering; but I think that when merged with UML, it becomes very clear and easy to apply.)

The scopes to which I believe this Five-Step strategy may be applied can be summarized in Table 2-1.

Table 2-1. Five-Step UML at Varying Scopes of Analysis and Design

ScopeUsage
Business

At this scope, you’re analyzing the organization and functions of an entire business or organization, with a focus on its interactions with other businesses, governments, etc. 

Actors reflect broad categories of external entities: governments, vendors, suppliers, customers, etc.

Use cases reflect the basic operations of the organization.

Structural elements reflect the business units or departments that carry out the operations.

Service

At this scope, you’re analyzing the organization and functions related to particular services of the organization. For instance, if the service is Purchasing, then operations might include ordering, tracking, receiving, and returns. (This scope, falling somewhere in between Business and Domain, won’t be necessary except for larger organizations.)

Actors reflect the individuals and systems that require and carry out these operations.

Use cases reflect the services performed by the business units or departments.

Structural elements reflect broad categories of domain objects that must be maintained, subunits of departments that carry out particular operations, and facilities where operations take place.

Domain

At this scope, you’re analyzing particular domain objects that must be represented or maintained in the system.Actors reflect the individuals who require the domain objects, along with the individuals who maintain them.

Use cases reflect the maintenance and transfer of domain objects (documents, schedules, etc.) that support the services of the business units.

Structural elements reflect the specific domain objects maintained, particular teams that carry out operations, and particular machines or devices used in maintaining or producing domain objects.  

System

At this scope, you’re analyzing the workflows involved in maintaining particular domain objects, along with the user interfaces and components that support those workflows.

Actors reflect the individuals who implement the workflow.

Use cases reflect the steps in the workflow. Use cases may also reflect detailed user interface interactions, when this helps to explain the user interface.

Structural elements reflect the user interfaces, interfaces to other systems, the components that provide the interfaces and user interfaces, other components, and interfaces between the components.

Implementation

At this scope, you’re designing particular components of the system.

Actors reflect persons and other systems and other components that interact with the component.

Use cases reflect the requests made through interfaces and user interfaces.

Structural elements represent classes and interfaces within each component.

Class

At this scope, you’re designing particular classes.

No actors.

No use cases, simply class operations; but these may be analyzed in a fashion similar to use cases.

Structural elements represent attributes, associations, dependencies, and interfaces of each class.

The strategy is important; it’s not just rigid rules about counting use cases. It’s a way of thinking about problems and communicating your thoughts and solutions.

{mospagebreak title=Step 5(c): Repeat (In Reverse)}

Expand the scope of your process to whole systems (designed with Deployment Diagrams). Repeat Steps 1–4 as appropriate for the current scope.

This optional step is for distributed systems: systems that must be deployed to multiple nodes. In this step, you’ll expand your scope to look at an entire system made up of the components identified in Step 4. By zooming out to the scope of the entire system, you can apply a similar Assign-and-Design process to determine the structure of the system.

UML Notation

In UML, the structure of a system is displayed using Deployment Diagrams, which consist primarily of two types of elements: nodes and associations.

Nodes

A node represents any hardware system in which your components may be installed or with which your components may interact. It may be thought of as a large component, realizing the interfaces of the components installed on it (though the only significant interfaces for a Deployment Diagram are those accessed by components on other nodes).

In a Deployment Diagram, a node is depicted as a cube with shaded top and right edges.

Nodes: A .NET Perspective

In .NET code, a node is primarily any machine to which you deploy .NET components. At the time of this writing, that means mostly Windows PCs (and soon, I hope, the Pocket PC platform as well). But there are ongoing efforts by third parties to port .NET to other environments and platforms.

And of course, there are other nodes besides Windows machines even in today’s .NET apps, including

  • The devices—printers, modems, etc.—with which your code interacts

  • Other computers that access an ASP.NET site via the Web

  • Other computers that access Web services via SOAP

Associations

In a Deployment Diagram, an association represents a connection between two nodes through which two components may communicate (via their interfaces). It is depicted on a Deployment Diagram as a solid line connecting the nodes.

Deployment Associations: A .NET Perspective

In .NET code, the associations between nodes will represent concrete mechanisms like Ethernet and R/F, hardware protocols like TCP/IP and 802.11b, and software protocols like SOAP and .NET Remoting.

Deployment Diagrams

Given these elements, then, a Deployment Diagram depicts nodes and associations between them. Figure 2-62 is a Deployment Diagram that depicts the nodes and associations that may be useful in the Kennel Management System.


Figure 2-62.  Deployment Diagram of the Kennel Management System

In addition, you may choose to list within or under each node the components that are deployed to that node. Such a diagram is illustrated in Figure 2-63.


Figure 2-63.  Deployment Diagram of the Kennel Management System, with components

Looking at the Deployment Diagram and at earlier Component Diagrams, you can see which interfaces must be remote interfaces. For instance, Care Giver Notify is realized by the Paging System, which is deployed to the Care Giver Station; but it’s accessed by the Check Out Form, which is deployed to the KMS Server. Therefore, Care Giver Notify must be a remote interface, and we must define a remoting protocol for it.

Note also that Check In Form and Check Out Form are deployed to the KMS Server, not to the Owner’s PC. This reflects our intention that these should be Web user interfaces: no code is actually delivered to the Owner’s PC, just HTML pages. The actual components that generate the HTML reside on the KMS Server. This also allows us to provide the identical interface to the Reception Station to support walk-in reservations.

Logical Deployment vs. Physical Deployment

The diagram in Figure 2-63 is an example of a logical Deployment Diagram: it depicts the components, the nodes to which they are deployed, and the necessary communication between the nodes. You may also wish to produce a physical Deployment Diagram that depicts the physical mechanisms by which the nodes are connected (usually without showing the components). This will help you to plan hardware acquisitions and connections.

In a physical Deployment Diagram, it is very common to use custom stereotypes that reflect the type of each node. You may also label the associations to describe how two devices are connected if it isn’t clear.

For example, Figure 2-64 is a physical Deployment Diagram for the Kennel Management System.

In this final exercise of the chapter, you and your team can wrap things up by creating Deployment Diagrams from the components and interface dependencies you’ve identified in previous exercises. The steps are summarized in the section that follows.


Figure 2-64.  Physical architecture of the Kennel Management System

 

Designing Deployment: A Summary

Start the process by looking through your components and assigning them to an appropriate node.

If you don’t have an existing node that should host that component, then you’ll need to add a new node icon to the Deployment Diagram. List the component under the appropriate node icon.

Next, based on the interface dependencies identified in the Component Diagrams, add appropriate associations between the nodes.

Finally, redraw the Deployment Diagram, adding appropriate devices and associations to produce a physical Deployment Diagram.

TIP To learn more about Deployment Diagrams, see Chapter 11.

{mospagebreak title=Summary}

In this chapter, we’ve worked through a complete iteration of each step in Five-Step UML. Along the way, I showed the new UML notation that you’ve needed at each stage of the process. The UML we’ve used in this chapter is by no means the complete language, but an exhaustive tutorial in the entirety of UML16 is not the aim of this book.

The UML notation that we’ve focused on in this chapter has been

  • Use Case Diagrams, with actors, use cases, and domain objects.

  • Activity Diagrams, with initial states, activity states, and terminal states, all joined together by transitions, branches, merges, joins, and forks. We also looked at how to assign different activities in an Activity Diagram into swimlanes.

  • Component Diagrams, with components, interfaces, realizations, and dependencies.

  • Class Diagrams, with classes divided up to include information on attributes and operations, and the connections between classes defined by associations and dependences.

From this point forward, I’ll assume you understand UML well enough to understand any diagram that I’ll draw, introducing any new notation as we come across it. If you’re still not comfortable with UML, I recommend that you do some more Five-Step exercises, and solve problems with your team. Practice with UML won’t make you perfect, because “perfect” is an unattainable goal; but practice is certainly the best way to learn. Remember, you don’t need to learn everything immediately. Like any language, you’ll pick more of it up as you use it, and become proficient more quickly with some parts of the language than others.

As you worked through the exercises in Five-Step UML, you should have got a taste of the power of UML. You’ve seen how you can apply a straightforward development process of define, refine, assign, and design, repeating it at different scopes in your system. Continue to practice this simple OOAD approach until you feel comfortable deriving classes and components and nodes and ultimately code, all from user requirements. At this stage, you aren’t defining these elements in enough detail to lead to implementation. Instead, you’re simply learning to think in UML.

16. For an exhaustive tutorial, I recommend Martin Fowler and Kendall Scott, UML Distilled, Second Edition (Addison-Wesley, 1999), followed by James Rumbaugh, Ivar Jacobson, and Grady Booch, The Unified Modeling Language Reference Manual (Addison-Wesley, 1999).

In Part Two, I’ll drill down into Five-Step UML in more detail, dedicating a chapter to each step of the process as we work through the design of the KMS in depth. Along the way, you’ll see more details of the UML notation that will allow you to very precisely design your system.

Before we delve further into Five-Step UML, however, let’s take a break and have a look at some general modeling advice. The set of pragmatic guidelines presented in the next chapter are not only applicable to the Five-Step process described in this book, but also to modeling in general.

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri Ensest hikaye