A crash course in UML state machines: Part 2
However valuable abstraction in itself might be, you cannot cheat your way out of complexity simply by hiding it inside composite states. However, the composite states don't simply hide complexity, they also actively reduce it through the powerful mechanism of reuse (the "Ultimate Hook" pattern). Without such reuse, even a moderate increase in system complexity often leads to an explosive increase in the number of states and transitions. For example, if you transform the statechart from Figure 2.5b to a classical flat state machine,5 you must repeat one transition (from heating to "door_open") in two places: as a transition from "toasting" to "door_open" and from "baking" to "door_open." Avoiding such repetitions allows HSMs to grow proportionally to system complexity. As the modeled system grows, the opportunity for reuse also increases and thus counteracts the explosive increase in states and transitions typical for traditional FSMs.
Behavioral inheritance
Hierarchical states are more than merely the "grouping of [nested] state machines together without additional semantics".6 In fact, hierarchical states have simple but profound semantics. Nested states are also more than just "great diagrammatic simplification when a set of events applies to several substates".7 The savings in the number of states and transitions are real and go far beyond less cluttered diagrams. In other words, simpler diagrams are just a side effect of behavioral reuse enabled by state nesting.
The fundamental character of state nesting comes from the combination of abstraction and hierarchy, which is a traditional approach to reducing complexity and is otherwise known in software as inheritance. In OOP, the concept of class inheritance describes relations between classes of objects. Class inheritance describes the "is a ..." relationship among classes.
For example, class Bird might derive from class Animal. If an object is a bird (instance of the Bird class),it automatically is an animal, because all operations that apply to animals (e.g., eating, eliminating, reproducing) also apply to birds. But birds are more specialized, since they have operations that are not applicable to animals in general. For example, flying() applies to birds but not to fish.
The benefits of class inheritance are concisely summarized by Gamma and colleagues:8
"Inheritance lets you define a new kind of class rapidly in terms of an old one, by reusing functionality from parent classes. It allows new classes to be specified by difference rather than created from scratch each time. It lets you get new implementations almost for free, inheriting most of what is common from the ancestor classes."
As you saw in the previous section, all these basic characteristics of inheritance apply equally well to nested states (just replace the word class with state), which is not surprising because state nesting is based on the same fundamental "is a ..." classification as object-oriented class inheritance. For example, in a state model of a toaster oven, state "toasting" nests inside state "heating."
If the toaster is in the "toasting" state, it automatically is in the "heating" state because all behavior pertaining to "heating" applies also to "toasting" (e.g., the heater must be turned on). But "toasting" is more specialized because it has behaviors not applicable to "heating" in general. For example, setting toast color (light or dark) applies to "toasting" but not to "baking."
In the case of nested states, the "is a ..." (is-a-kind-of) relationship merely needs to be replaced by the "is in ..." (is-in-a-state) relationship; otherwise, it is the same fundamental classification. State nesting allows a substate to inherit state behavior from its ancestors (superstates); therefore, it's called behavioral inheritance.
The concept of inheritance is fundamental in software construction. Class inheritance is essential for better software organization and for code reuse, which makes it a cornerstone of OOP. In the same way, behavioral inheritance is essential for efficient use of HSMs and for behavior reuse, which makes it a cornerstone of event-driven programming. The book this series is excerpted from--Practical UML Statecharts in C/C++, Second Edition--presents, a mini-catalog of state patterns shows ways to structure HSMs to solve recurring problems. Not surprisingly, behavioral inheritance plays the central role in all these patterns.


Loading comments... Write a comment