Constructing the Communication Diagram


You place the messages used to perform the collaboration on the basic diagram of the participants. Each message, which is a communication between a sender object and a receiver object, is indicated on a line connecting the two of them.

The whole diagram is enclosed in a frame and you use the abbreviation sd to stand for your communication diagram.

 Warning   You may be wondering why the abbreviation for a communication diagram is sd and not cd. We’ve wondered about that, too—and we’ve complained. Looks like this must have been one of those silly compromises that got made when the UML gurus got too tired. They wanted all the interaction diagrams to have the same abbreviation—to simplify things. And they didn’t want to use id or int because they thought those would be confusing. That’s why we have to live with sd as the abbreviation for sequence diagram, communication diagram, timing diagram, and interaction-overview diagram. The gurus can always justify using sd by saying that a communication diagram is a type of sequence diagram. With any luck, an early revision to UML 2 may yet fix it. In the meantime, if all that ambiguity bothers you, you may want to use cd as your abbreviation for communication diagram (provided your UML tool allows it).

The name of the communication diagram is the name of the use case or operation that you are diagramming. Because you are typically doing design when you make a communication diagram, you should consider taking a more formal approach to documenting the arguments and return values of the interaction. In Figure 14-5, we name the interaction based on the use-case name GenerateBill(rmNum:RoomNumber, out newBill:Bill).

With this as a name, you indicate that the GenerateBill interaction takes a RoomNumber as input argument—and that inside the interaction, this argument is called rmNum. There is also an output argument (of type Bill) that will be called newBill inside the interaction. Normally, if you create an object inside an interaction and it has to be visible outside, you also indicate it as an out argument or a return.

Numbering steps sequentially

Message syntax on a communication diagram is essentially the same as for the sequence diagram. (You can find more information on this syntax in Chapter 12.) The first key difference you notice is that on your communication diagram, the messages are numbered—and each message is executed in sequential order. By examining Figure 14-5, you can see that the following steps are executed in this order:

  1. thisRoom=getRoom(rmNum): First, the GenerateBill controller asks the HotelInventory container class to find the correct Room object with the given rmNum. The correct object is returned and placed in an attribute within the GenerateBill controller named thisRoom. The HotelInventory object can find the correct Room because this relationship is indexed/qualified by roomNumber (See the Figure 14-3).

  2. occFlag=isOccupied(today): Next, the GenerateBill controller queries the Room to see if it isOccupied(today). The GenerateBill controller can send the message to the room because the query is called on the Room object that is was returned from call #1.The notation thisRoom at the end of the message line reminds you of the way the GenerateBill controller knows about the object. The results from the query are returned and stored in an occFlag (short for occupationFlag), which is a local attribute of the GenerateBill controller.

    This is a good example of how designing the messages can cause structural changes to the class diagram. Because the GenerateBill now knows about a Room object, we may decide that there is a link between the two objects. We cover this and other approaches in Table 14-1.

  3. [occFlag] newBill = Bill(thisRoom, controller=self): Next, the GenerateBill controller queries the Bill and tells it how to find the room by passing it the thisRoom argument. The controller has this value because call #1 returns it. But, before the call can be initiated, the controller checks the guard condition [occFlag], which was returned from call #2. If the call is performed, the Bill object is returned as newBill, which matches the return argument of the interaction.

    The controller also creates a reference to itself and passes that to the Bill. This reference will be used in the next call (call #4) so the Bill can find the controller again. Self is a reserved keyword, representing the calling or executing object.

  4. billReady(self): Lastly, the Bill object calls the billReady() operation on the GenerateBill controller and passes a reference to itself back to the controller. The Bill is able to find the controller because the controller was passed in call # 3.


    Figure 14-5: Initial communication

Outlining procedural calls

Communication diagrams give you the numbering capabilities to display graphically the calls to operations—and then the calls from those called operations, and (in turn) the calls from the operations they call, and so on. If you can keep your head from spinning, you can identify as many levels of calls and operations as you need (or at least as many as will fit on the diagram).

This miracle is done by using a tool you’ve seen if you’ve ever examined a table of contents: an outline-numbering scheme. If an object gets a message to execute an operation that is numbered 3:, any messages it then issues (numbered 3.1:, 3.2:, or 3.3:) are subordinate messages because they’re issued within the context of 3:. Accordingly, any message starting 3.x: must complete its business before the top 3: message can be considered complete. This follows the traditional outline numbering pattern shown below:

 3:    3.1:    3.2:       3.2.1:       3.2.2:    3.3:    3.4: 4: 

In Figure 14-6, we use some outline numbering of the messages. Examine (for example) message 2, where the GenerateBill controller asks the Room object if it isOccupied. To accomplish this work, the Room object also calls an operation on another object; in this case, it calls an operation on the latest Stay object ([latest]:Stay). Because this operation is subordinate, it needs a lower-level outline number. You would use 2.1, because this is the first (and only in this case) subordinate operation within operation 2. In the example, this operation on the Stay returns an occFlag to the Room if the latest stay included today. The Room, in turn, returns the occFlag back to the GenerateBill controller. When you use this outline-style technique of numbering, you can detail how each operation works and calculate results for its caller.


Figure 14-6: A communication diagram with outline numbering.

Message 3: also has some subordinate steps:

 3[occFlag]: newBill = Bill(thisRoom, controller=self)    3.1: thisStay = getStay(today)    3.2: party = getParty    3.3: getDayRange(Bill.sd = sd : startDate,          Bill.ed = ed : endDate)    3.4: getTotalCharges(sd, ed)       3.4.1*: getLodgingCharge() 

This sequence of messages is governed by the guard condition on message 3. If the [occflag] is false, the whole sequence beginning with 3 is skipped. If [occFlag] is true, then message 3 is sent to create the Bill. Then (as the diagram says), the Bill sends message 3.1 to the Room and follows up with message 3.2, 3.3, and 3.4 to the Stay. As any number of levels can be used, message 3.4.1* getLodgingCharge() is sent by the Stay to the Lodging.

Looping

In Figure 14-6, you may see that there is a message with an * in the sequence number, 3.4.1*: getLodgingCharge(). This * indicates that many instances of that message are sent with that same number. We recommend thinking of this * as a multiplicity indicator, similar to that used on UML associations. If there’s just a *, it indicates that the message is to be repeated as often as needed. If you repeat a message, then you also repeat all its subordinate messages.

If you want to have the message repeated a specific number of times, the syntax is as follows:

 SequenceNumber*[iteration clause]: 

The iteration clause has several common forms:

  • Boolean expression: The expression repeats as long as the expression is True. A message such as 3*[isMoreNeeded] would continue until isMoreNeeded=False.

  • loopVariable=lowerLimit..upperLimit: This expression initializes the loopVariable to the lowerLimit and sends the message. Then the loopVariable is incremented and tested against the upperLimit. As long as the loopVariable is in range, the message is sent again. These upper and lower limits may be integers or ordered enumerations of values. For example, the messages 4*[thisMonth=Jan..Dec] and 4*[thisMonthNumber=1..12] would both execute 12 times.

  • Codelike looping syntax: UML allows you to write the iteration clause using the target programming language. Although there is some value to this practice, I wouldn’t recommend tying your model to your programming language; after all, the language could change in the future. Also, your UML tool may not understand the syntax exactly—so it probably won’t generate high-quality code.

In Figure 14-7, I’ve used the loopVariable approach in two locations. Look at message 3.3 from the Bill to the Stay. This tells us how the returned out arguments (sd and ed) are set and where their results go. Upon return, the two arguments, that of sd and ed, are set to the startDate attribute of the Stay and the endDate attribute for the Stay. Then, these values are saved in the Bill as Bill.sd and Bill.ed. Later, in message 3.4*, the Bill uses the sd and ed as (respectively) the lower and upper limit for a loop. The Bill sets up a loop with a loopVariable of thisDay and asks the Stay to retrieve the total charges for this day, via the call 3.4*[thisDay=sd..ed]: getTotalCharges(thisDay).


Figure 14-7: A communication diagram with looping.

Message 3.4.1 is sent inside this loop to [thisDay]:Lodging, which illustrates that the loopIndex value, thisDay, (being passed in as a parameter in 3.4*) is being used by the Stay to find (or select) the correct Lodging. Within 3.4.1, the Lodging asks the RoomRate object for information on the rate. Every time the 3.4 loop iterates, you have message 3.4.1 sent, and then message 3.4.1.1 is sent.

start sidebar
Looping or selecting?

Often an ambiguity can crop up when you send multiple messages to a specific lifeline (a part reference) on of a communication diagram. A lifeline refers to a participating instance, but the naming structure allows the reference to point to different instances in each loop iteration. Such a sending could mean that there are many messages, each sent to a separate instance, or it could mean that there are many messages, all sent to the same instance. Fortunately, you have several ways of trying to clear up this problem.

One common approach that was possible in UML 1.x was to indicate that the destination is a multiobject. Unfortunately, the UML gurus have eliminated this feature from UML 2, but many tools will still support it and it may be re-inserted in UML 2.1.This technique involves making the target lifeline box look as if there’s a stack of objects slightly offset. I’ve used this approach in Figure 14-6 on the Lodging. When you use this notation coupled with an * in the incoming message, you’re indicating that the loop of messages is over different objects in the set.

A better solution, though only possible when you are using an explicit loop counter, is to make the loopVariable part of the selector of the lifeline. As an example, imagine that you want to create an array of the number of working days per month for a payrollCalculator. In the figure in this sidebar, there is a message that loops over the months and asks each month for the number of working days.

Note carefully that the loop index is used as the selector of the Month—and as the subscript of the return value from the call. This technique is very powerful; it allows specific identification of the elements of target lifelines, arrays, or any ordered collection. Of course, you can only use the loopVariable as a selector if the lifeline (in this case, the Month) is ordered in one of two ways:

  • As an array, where the specific elements are indexed or referenced by a numerical value, such as 1..12

  • Addressable by an enumerated qualifier that already has a defined order, such as the values, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec

    You may have to go back to the class diagram that contains the Month class and make sure that there is such a qualifier or an ordered relationship that you can traverse to find the correct month.

end sidebar

Messages 3.4.2 and 3.4.3 are also sent within the 3.4 loop. Message 3.4.2 is an operation call sent by the Stay to itself, to return the number of Charge objects associated with it for the current day getNumCharges(thisDay). The result returns as numCharge.

This result is then used to construct another loop—an inner loop that uses the index thisCharge and loops from 1 to the numCharges. As both loops—thisDay and thisCharge—are going on at the same time, you can use both loop indices to select the charge on which you want to operate.




UML 2 for Dummies
UML 2 For Dummies
ISBN: 0764526146
EAN: 2147483647
Year: 2006
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net