Simplifying Large State Diagrams


Creating a state diagram for an object with simple dynamics is easy. You usually have an initial state, a wait state, a few important event transitions to states with important behavior, and a final state. But, with more dynamic objects you may notice the following characteristics:

  • The same entry, exit, and internal events are repeated in several different states.

  • The same event transition is coming from several different states—but all going to the same state.

  • A couple of different do activities can happen at the same time but completely independent of each other.

  • There are very complex activities within a state that also depend on important events.

  • Interruptions cause your object to stop what it’s doing. Then the interruption must cause a complex method to execute without further interruption. And after the interruption is handled, allow the object to pick up with what it was doing before the interruption.

Don’t be surprised if your state diagram tends to sprout an awful lot of lines, repeated event transitions, and many states that all do the same thing. Fortunately, you don’t have to have all this repetition. You can solve these problems by employing the following techniques:

  • Generalize your states: Arranging states to emphasize their commonality of events and behaviors helps simplify the diagram.

  • Build submachines: Creating separate mini-state diagrams, which you can reuse in your state diagrams, makes your diagrams easier to understand and easier to maintain.

  • Utilize pseudostates: Using a special shorthand notation reduces the number of states and transitions you have to depict for certain situations.

  • Show concurrency: Illustrating concurrency—independent behavior—within an object by establishing separate regions inside the same state makes for a more compact diagram. (Some of your objects can walk and chew gum at the same time.)

Generalizing states

Each of your states has at least one activity that the object does when the object is in that state. An activity is some major behavior performed by an object that takes time. If this activity involves a complex sequence of behavior you can show that activity with a state diagram inside the larger state. The states shown within a state are known as substates. The “superstate” containing the substate is also known as the generalized state. When you have to describe an activity within a state as a state diagram, simply expand the surrounding state and place your substates inside. This type of UML diagram looks like someone’s put a state diagram inside another state diagram.

Figure 18-1 shows an example of a simple state (Archiving) and its primary behavior (do / saveAccountData). If an instance of CustomerAccount is in the Established state and the close event occurs, then the object makes a transition to the Archiving state. Another way into the Archiving state is from the Canceling state when the canceled event occurs. (Note that these two distinct transitions have one destination.) Once in the Archiving state, the account data must be saved. When the saved event occurs, the object makes a transition out of Archiving and into the final state.


Figure 18-1: Simple archiving state.

The process for saving account data is complex and involves some important events. To archive an account requires that all transaction processing come to a halt, then the account gets locked, then officially closed, and finally all the data associated with the account gets backed up. You can show the detailed sequences for the Archiving behavior as substates within the Archiving superstate.

Figure 18-2 demonstrates what this process looks like:


Figure 18-2: States within states.

  1. An instance of CustomerAccount, upon entering the Archiving state, makes an automatic transition from the initial state (the large black dot) to the Locking state.

  2. As the object enters the Locking state, the entry action requestLock takes place. Transactions are halted and the object waits for the locked event to occur.

  3. Upon receiving the locked event, the object makes a transition to the Closing state where it formally closes the account so no more transactions can take place on this account.

  4. When the account is shut down and the object receives the closed event, the object enters the Backup state.

    In the Backup state, the object must log in to the database, and then insert account data into the database. When there is no more account data to insert, the object receives the lastTransaction event.

  5. The lastTransaction event stimulates the object to exit the Backup state; the object performs the exit action and logs out of the database.

  6. While the object makes its transition from the Backup state within the Archiving state, the saved event is sent to the object playing the role of self. (In effect, the object sends itself the saved event.)

  7. When the saved event is received at the higher-level state (as diagrammed in Figure 18-1), the whole object to makes the transition to the final state.

 Warning   Look out for repeating substates. Sometimes an object must do the same thing at several different points in its life. You could end up creating the same substate diagram for several different superstates. If this starts to happen to you, use submachines.

Using submachines

Submachines are really mini state diagrams you can include in other state diagrams. This ensures that you don’t have to repeat yourself. For example, each instance of the CustomerAccount class has several states—OnTrial, Established, Canceling, and Renewing—that must handle the statement event. When the statement event occurs, the CustomerAccount object must perform generateStatement.

However, generateStatement isn’t a simple action. Generating a statement is dependent on customer information, transaction data, the day of the year, and whether there are any overdue invoices. To address this issue, you could create a substate diagram for generateStatement and place it inside the OnTrial, Established, Canceling, and Renewing states. However it’s more efficient to create a submachine for generating a statement, and then include it in those four states.

 Remember   Follow this process to make use of submachines:

  1. Recognize the need for a submachine.

    You may want to create a submachine if either of the following is true:

    • You are repeatedly using the same group of substates inside several different superstates within the same object’s state diagram.

    • You see a mini state diagram within the different state diagrams belonging to objects of separate classes.

    In our example, we recognize that a submachine is warranted because the states for generating a statement are reused in the OnTrial, Established, Canceling and Renewing states of CustomerAccount objects.

  2. Build a submachine.

    Pull out the common mini state diagram and create a separate state diagram. This state diagram has one superstate with the common substates inside it. You have to give the superstate a name.

    We named the submachine’s superstate GenerateStatementSM for the CustomerAccount example. Figure 18-3 shows the UML notation for the submachine GenerateStatementSM. The submachine contains the WaitForCustomer, ObtainingTransactions, Summarizing, TransactionFormating, and GenerateOverDueNotice substates. Every time you include the GenerateStatementSM in other states, the exact sequence for generating statements based on customer information, the day of the year and checking for overdue invoices is performed.


    Figure 18-3: The Generate Statement submachine.

  3. Include the submachine.

    Now that you have a submachine you can use it wherever you need it. This is done with a special include statement. In the state that has the submachine, place the word include followed by a slash (/) followed by the name of the included submachine.

    Figure 18-4 shows how we used the GenerateStatmentSM within the Canceling state of a CustomerAccount instance. We created a substate, Wait for Cancel, so the object can wait for the account to be canceled. If while it’s waiting, a statement event should occur then the object transitions to the HandleStatement state. Because it “includes” a submachine, the GenerateStatementSM submachine is executed.


    Figure 18-4: Including a submachine.

    When the GenerateStatementSM completes at its final state, the object will automatically transition back to the Wait for Cancel state.

 Tip   You use submachines to describe common “event/action” sequences such as handling errors, providing help, reading data and writing data.

Inheriting events in substates

When you create substates within a superstate, your substates inherit flow of control from the superstate. An event that stimulates your object to exit a superstate also causes your object to exit any substate it may be in. If you have a transition that goes directly to a substate from outside the superstate, then the entry actions are executed in sequence from the outer most superstate to the inner most substate. The opposite is true for exiting a substate to another state outside an enclosing superstate. The object executes any exit actions in sequence from the inner most substate to the outer most superstate. Any internal events on superstates are inherited by the substates. They will interrupt the current substate.

If you’re an object-oriented programmer, this may sound familiar. This is parallel to how “new” operations are done when you create an instance of a subclass. First the new operation of the superclass is performed, followed by the new operation of the subclass. Your object performs the destructor operation of the subclass and then the destructor operation of the superclass.

Figure 18-5 shows a piece of the state diagram that describes instances of the CustomerAccount class. In this diagram, we have included the OnTrial, Established, and Renewing states as substates within the ManageTransactions superstate. When the object receives the validated event, it transitions directly to the OnTrial state. First the entry action TransactionManager.notify(this) is executed and then the entry action accountStatus := OK is executed because the entry actions are inherited from outermost to innermost actions.

Figure 18-5 also uses flow-of-control inheritance to reduce the complexity of the diagram. Instead of having individual transitions from OnTrial to Canceling, from Established to Canceling, and from Renewing to Canceling, you need only one transition. The ManageTransactions superstate has such a single transition—cancel—and it goes straight to the Canceling state. All substates inherit this same transition. Thus, when the object receives the cancel event—no matter what substate it occupies within ManageTransactions—it makes the transition to the Canceling state.


Figure 18-5: Inheriting events.

If you refer to Figure 17-7, you can see the OnTrial state and the Established state, both with the deposit and withdrawal internal events. If you use flow-of-control inheritance, you only have to show them once—as internal events in the superclass. (Because you cannot make a deposit or withdrawal when the account is being renewed, we had to “defer” those operations in the Renewal state.)

Utilizing pseudostates and saving history

As you build more complex state diagrams, you can make use of some shorthand notation we call pseudostates that provide you with common ways to hook transitions together. Chapter 16 introduces a couple of pseudostates —the initial state and the final state. The initial state is indicated on a diagram; its large dot and transition to some other state serve as shorthand for Start here when you enter this state diagram. It’s simpler than having a regular rounded rectangle represent a state with a name like Please start here (you’d have to get everyone who reads your state diagrams to know that the Please start here state where you always start your state diagrams). With more complex state diagrams you have to connect up many transitions to form complex paths through the different states in your objects. To help you, UML provides pseudostates for connecting these transitions.

Most of the time your objects are happy to be interrupted by some important event. The event stimulates your object to move on to some other state to do some other activity, happy never to return to its previous state. While a CustomerAccount object is in the ManageTransations state, it’s either in the OnTrial, Established, or Renewal substates. But, you have to interrupt the object so it can produce a statement. When the statement is produced, you have to get your object back to the substate it was in before the interruption. That means saving the history of what the object was doing so you can get back to it.

You save the history of a state so you can get back to it later with the history pseudostate. Actually there are two kinds of history pseudostates:

  • Shallow history: UML shows this pseudostate with a capital H inside a small circle. The shallow history pseudostate captures information about the current state but not any of its substates.

  • Deep history: When you want to capture information about the current state and all its substates, then you use the deep history pseudostate. This is shown as a capital H followed by an asterisk (*) inside a small circle.

  • Your history pseudostate has a transition from the state that handles the important interruption to the history pseudostate. You can also include a transition from the history pseudostate to the default state within the superstate: Any incoming object that has never been in the superstate before makes an automatic transition to the default state. As an example, Figure 18-6 shows you how to handle the statement event when it happens during the ManageTransactions state.

Here’s the play-by-play sequence shown in Figure 18-6:


Figure 18-6: Using the history pseudo-state.

  1. A statement event stimulates the object into a transition from the ManageTransactions state to the Handle Statement state.

  2. When the Handle Statement state is finished, control passes to the history pseudostate.

  3. From there, the object continues in whatever state it occupied before the statement event interrupted.

  4. If no substate of ManageTransactions was active at the time of the interrupt, then the default state pointed to by the history pseudostate (OnTrial) is activated.

 Remember   Use internal events to handle simple interruptions to your object’s behavior. These events occur while an object is doing some activity within a state—and they don’t cause the object to exit that state. (See Chapter 17 for more information on internal events.)




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