- Interaction Diagrams
- Sequence Diagrams
- Collaboration Diagrams
- Conclusion
- Source Code
Sequence Diagrams
UML sequence diagrams typically show some sequence of method invocations that achieve some specific purpose. Figure 2 shows a sequence diagram for calculating the total of a sale transaction. It starts with a call to the method calcTotal() of the Sale class. The relevant snippets of source code are shown below the diagram.
Terminology Note
UML defines an operation as the signature of a method. The term method is reserved for the code that provides the implementation of an operation. In the Java world, it is usual to use term method in both contexts. In sequence diagrams, the invoking of an operation is called sending a message. Sequence diagrams are essentially about the implementation of operations, so I have used the term method throughout this article (occasionally resorting to the term message when describing a particular UML diagram).
Figure 2 A sequence diagram for calculating the total of a sale.
/** * From the Sale class: * calculates the total of the sale from the lineItem subtotals * @return total of the sale */ public double calcTotal() { total = 0.0; Iterator i = lineItems.iterator(); while (i.hasNext()) total += ((LineItem)i.next()).calcTotal(); return total; } /** * From the LineItem class: * calculates the cost of this amount of this kind of item * @return the cost of this amount of the item */ public double calcTotal() { total = product.calcTotal(this); return total; } /** * From the Product class: * calculates the current cost of a quantity of the product * @return cost of the line item supplied */ public double calcTotal(LineItem li) { return amount * li.getQuantity(); }
To get a better overall feel for the sequence, only the method names are shown. More detailed sequence diagrams show method arguments and return types.
Objects that participate in the sequence and exist at the start of the sequence are spread across the top of the diagram. They are displayed using the usual UML notation for an object; the same shape or symbol used for the class of the object (a rectangle by default) with the name of the object followed by a colon, and the name of the class that defines that object. The whole name is underlined (for example, aProduct:Product in Figure 2). Either the object name (for example, :Sale in Figure 2) or class name (for example, Sender in Figure 2) may be omitted, but obviously not both. If the object name is omitted, the colon must be retained.
Time is imagined as running vertically from top to bottom of the diagram. Each object has a lifeline running vertically down the page, immediately below its rectangle. Method invocations are drawn as solid lines with open arrowheads from the lifeline of the calling object to the lifeline of the receiving object. An object's lifeline is widened whenever one of its methods is being executed. These activation bars may be nested to show that another method of the object has been invoked as part of the execution of the previous method; the getQuantity() method in Figure 2 is an example of this.
Optionally, returns from methods may be shown as a dotted line with an open arrowhead (for example, the return arrow from :Sale to Sender in Figure 2) where it makes things clearer to do so.
Where iteration over a collection of objects is needed, an asterisk precedes the method name with an optional condition inside square brackets. An example can be seen in the call from the Sale class to the LineItem class objects in Figure 2.
As with UML class diagrams, what would normally require the inspection of multiple source code files is summarized in one UML diagram. Reverse engineering sequence diagrams from existing source code can help developers new to the code understand how it works, and can help developers communicate to client representatives the way the software works in a form less threatening than source code.
Sequence diagrams are also often used to help analyze requirements or document the design of a feature in a process such as Feature-Driven Development (FDD)1
Objects that I Know
When sketching a sequence diagram to explore the problem domain, explicitly prove a class diagram can support a sequence of method invocations that achieve a required goal, or as part of the low-level design of a functional requirement, a common mistake that must be avoided is to invoke a method on an object that the caller has no direct knowledge of. In Java, there are three fundamental ways an object may know of the existence of another object. First, an object's static or instance variables may contain either a reference to the target object, or a name or identifier through which the object might be located via a lookup service of some description (for example, JNDI, Factory class, database query). In this case, this knowledge is usually shown as an association in a class diagram. Second, the calling object may be passed a reference to the target object as a parameter of the invoked method. Third, the calling object may create a new instance of the target object.
More Notation
Figure 3 shows a sequence diagram for the complete() method of the Sale class. This time, we have numbered the messages. The complete() method calls two other methods of the Sale class: calcTotal() and calcPayments(). Figure 3 shows the loopback notation used to indicate that an object is calling method on itself.
Larger sequence diagrams can become too wide to display comfortably on a screen. In Figure 3, a switch in Together ControlCenter's2 options panel displays the class name underneath the object name instead of alongside, reducing the amount of horizontal space required for each object. This can reduce the amount of horizontal space required by the diagram if class names are longer than a few characters and arguably make the diagram a little easier to read. However, to be strictly compliant with the latest UML specification3, the class name should appear to the left of the object name and be preceded by a colon, as shown in Figure 2.
Notation Note
When working collaboratively, I enjoy the convenience of using three-inch square Post-It[tm]notes and marker pens on flipchart pads or whiteboards. I also use a common shorthand convention for the object and class name; form the object name by prefixing the class name with an"a" or "an" as appropriate, and omit the class name.
Figure 3 A sequence diagram for completing a sale.
As a result of the call to calcTotal() by the complete() method, our calcTotal() sequence in Figure 2 is a consequence of the complete() sequence in Figure 3. We could have simplified Figure 3 by omitting the Product object and its interactions with the LineItem object and by referring the reader to Figure 2 at that point. Again, the general principle of only showing what is necessary to accurately communicate with the reader applies. For example, few readers of a sequence diagram will benefit from the inclusion of standard Java classes such as iterators, wrappers, and collection classes. Also, although a sequence diagram can show the use of looping and branching constructs, this level of detail is perhaps best examined by reading the actual source code in conjunction with a higher-level sequence diagram. Figure 4 shows the result of using Together ControlCenter to reverse engineer the complete() method of the Sale class and asking it to include as much detail as possible. This level of detail is probably too much for most people. However, Figure 4 does serve one useful purpose: The exception object shows that objects created during a sequence diagram are drawn at the point they are created instead of at the top of the diagram.
Figure 4 A highly detailed sequence diagram generated by a tool.
As with class diagrams, there are many more notational nuances to sequence diagrams provided by the UML specification. However, those described above are generally enough to get started with.