A Stable Design for the State Design Pattern David Gore

International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
A Stable Design for the State Design Pattern
David Gore
Software Engineering Consultant, Munich, Germany
[email protected]
Abstract
The design of the state pattern is analyzed and the notion of stability of a design is
proposed. Mathematical models and ideas from numerical analysis are used to derive a
stable design for the state pattern. The stability of the design is illustrated by considering two
canonical examples from the OO literature.
Keywords: State Pattern, Finite State Machine, Stability, Granularity.
1.
Introduction
Design Patterns in software engineering have been developed as a result of years of
software development experience and provide design guidelines for software developers. In
general, the definition of a design pattern is guided by the principles of Object Oriented (OO)
Programming. The question is, other than software development experience; are there any
other practices or analyses which inspire a design pattern?
In this investigation we consider the state design pattern and ask the following questions:
by studying the mathematical models of a finite state machine (FSM) can a more OO design
of the state design pattern be motivated? Can criteria be identified to determine when the state
design pattern is stable with respect to changes in the FSM mathematical model? Can the
state pattern design be quantified, or can aspects of the design be measured?
To help answer these questions the concept of granularity of a design will be introduced.
Thereafter, the stability of a design will be defined. By examining the mathematical model of
a FSM, state diagrams, state transition tables and class diagrams a more OO design of the
state pattern will be developed.
The State Design Pattern
In [1], the state pattern is discussed using the example of a gumball machine. Here is the
state diagram.
Here, N is the number of gumballs. The initial state is the NoQuarterState. When the
customer inserts a quarter into the gumball machine the state changes to the HasQuarterState.
If the customer ejects the quarter then the gumball machine returns to the NoQuarterState. If
the customer turns the crank, the gumball machine goes into the SoldState and dispenses a
gumball to the customer. If there are still gumballs left in the gumball machine, the gumball
machine returns to the NoQuarterState. If there are no gumballs remaining, the gumball
machine goes into the SoldOutState, the final state.
33
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
stmState diagram gumball machine
i nsertQuarter
NoQuarterState
HasQuarterState
ej ectQuarter
di spense [N>0]
turnCrank
SoldState
di spense [N=0]
SoldOutState
Figure 1: State diagram for gumball machine
Design 0 The state machine is represented using a single class containing all necessary
states and actions and transitions.
In the gumball machine example, the GumballMachine class is the single class, the states
are represented using Enumerations, the number of gumballs, N, is initialized in the
constructor of GumballMachine and a method is defined for each state action: insertQuarter,
ejectQuarter, turnCrank and dispense.
As pointed out in [1], this implementation has many drawbacks. It is not very object
oriented, it violates the Open-Closed Principle (classes should be open for extension but
closed for change) and states and state transitions are hidden within the GumballMachine
class. The single class in Design 0 is a so-called “God object”; an anti-pattern which means
that too many functions are contained in a single part of the design or class.
To correct these problems the State design pattern is introduced. The definition of the
design pattern is, [1],
State Pattern Definition The State Pattern allows an object to alter its behaviour when
it’s internal state changes. The object will appear to change its class.
An interface IState is introduced defining all possible actions which may operate on a state
34
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
class IState
« in te rfa ce »
IState
+
+
+
+
in se rtQu a rte r() : vo id
e je ctQu a rte r() : vo id
tu rn Cra n k() : vo id
d isp e n se () : vo id
Figure 2: State interface for gumball machine
class gumball machine design 1
«i nterf ace»
IState
Gumball Machine
-
noQuart erSta te: IState
hasQuarterSt ate: IStat e
sol dState: ISt at e
sol dOut St ate: IState
stat e: I St ate
+
+
+
+
i nsertQuart er() : voi d
ej ect Quarte r() : voi d
turnCrank() : voi d
rel easeGum ba l l () : voi d
+
+
+
+
i nsert Quart er() : voi d
eject Quart er() : voi d
turnCrank() : voi d
di spense() : voi d
Sta te
NoQuarterState
-
gum bal l M achi ne:
Gum bal l M achi ne
+
i nsert Quart er() : voi d
Sta te
HasQuarterState
-
gum bal l M achi ne:
Gum bal l M achi ne
+
+
ej ectQuarte r() : voi d
turnCrank() : voi d
State
Sta te
SoldState
-
gum bal l M achi ne:
+
di spense( ) : voi d
+
+
+
+
i nsertQuart er() : voi d
ej ect Quarte r() : voi d
turnCrank() : voi d
di spense( ) : voi d
Gum bal l M achi ne
Sta te
SoldOutState
-
gum bal l M achi ne:
Gum bal l M achi ne
Figure 3: Class diagram for design 1 of the gumball machine
35
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
Each state is a class implementing this interface. For convenience an abstract class, State,
implementing IState is introduced. The abstract class contains the default implementations of
the actions. The GumballMachine class, in general, called the Context class, associates its
states and holds a reference to the current state. Additionally, it contains methods which
reflect the actions of the gumball machine: insertQuarter, ejectQuarter and turnCrank. When
one of these actions occurs, it is delegated to the appropriate state using the current state
reference,
public void insertQuarter()
{
state.insertQuarter();
}
The ideas of this design can be summarized as follows:
Design 1 The state machine is implemented using a Context class and an interface for the
states, IState. Each state implements this interface and the Context class delegates the state
actions to the state subclasses of IState.
For the gumball machine, the Context class is GumballMahcine and the class diagram for
this design is
Design 1 of the state machine delegates the processing to state objects. In this design the
states have been decoupled from the Context: the GumballMachine class no longer contains
actions or methods of a particular state. This design reflects the state diagram in Figure 1
more closely. Additionally, the long, unmanageable conditional statements have been
removed: passages of code which make the implementation difficult to understand and
difficult to change. These benefits and more have been identified in [1, 2].
A drawback of this design is that the individual states are dependent on one another
through their transitions, [2]. For example, the HasQuarterState is dependent upon the
NoQuarterState as seen in the following code snippet taken from the implementation of the
HasQuarterState class,
public override void ejectQuarter()
{
Console.WriteLine("Quarter returned.");
gumballMachine.State = gumballMachine.NoQuarterState;
}
This drawback leads to a re-analysis of the design.
Analysis
To motivate our analysis we consider the mathematical model of a finite state
machine (FSM), [3],
Mathematical Model A deterministic finite state machine M = (Σ, S, s0, δ, F) where
Σ = nonempty input alphabet, finite set of symbols
S = finite, non-empty set of states
s0 = initial state
δ = state transition function
F = set of final states, a possibly empty subset of S.
36
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
The state transition function is defined as δ: SXΣ → S, that is δ(p,x) = q, where p, q are
members of S and x = (x0, x1, …xn); where each xi is a member of Σ.
The inputs to the gumball machine are {insertQuarter, ejectQuarter, turnCrank, N},
where N denotes the number of gumballs. The dispense action is an internal action and
strictly speaking not a member of Σ. The input alphabet for the gumball machine is, Σ =
{insertQuarter, ejectQuarter, turnCrank N>1, turnCrank N=1}.
Denote s0 = NoQuarterState, s1 = HasQuarterState, s2 = SoldState and s3 = SoldOutState.
Then, the state transition table for the gumball machine is
Table 1: State transition table for the gumball machine
insertQuart
er
ejectQua
rter
turnCra
N>1
N=1
nk
s1
0
s0
s2
1
dispense /
2
s1
dispense /
s3
3
In the SoldState state, s2, the dispense action is executed first followed by either the
NoQuarterState or the SoldOutState, depending on the number of gumballs remaining. Table
1 clarifies the definition of the gumball machine’s state transition function, δ.
Before continuing we remark that the state diagram of the gumball machine in Figure
1 may be interpreted as a mathematical graph, [4],
Graph Model The state diagram for the gumball state machine is a multi-digraph G = (V,
E) with vertices V and directed edges E.
The states of the gumball machine are represented as the set of vertices, V = {s0,..., s3}.
The transitions of the gumball machine are represented as the set of edges, E. From Figure 1,
E = {(s0, s1), (s1, s0), (s1, s2), (s2, s0), (s2, s3)}, or, in general, for each e in E, e is an
ordered pair, e = (x, y) where x and y denote vertices. The Graph Model indicates that the
state diagram has two distinct mathematical entities; vertices and edges. These correspond to
states and transitions, respectively, in the FSM.
Another aspect of our analysis considers two ideas from the theory of numerical
analysis for differential equations. Generally speaking, the stability of a numerical method
depends on the approximating difference equation. When the difference equation is selected
in an unsuitable way, the numerical method will be unstable. In the same way, when a design
is selected in an unsuitable way it will be an unstable approximation to the underlying
mathematical model. Recall, we are interested in the stability of a design relative to changes
in the underlying model. This is important because, as pointed in [1], the only constant in
software development is change.
Stability Definition The design of the state pattern is stable when changes in the state
machine result in localized changes in its implementation.
37
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
A stable design is one that “localizes” changes in the underlying model. The focusing of
changes to specific areas of the implementation is an important aspect of a stable design.
Stable designs are more OO because all important model entities inherently satisfy the One
Class One Responsibility principle, [1]. Stable designs reduce testing by localizing it and
simplify code maintenance.
Secondly, in numerical analysis, the stability of a solution method given by a
difference equation depends on the mesh size of the solution grid. An example is the von
Neumann stability analysis, [5]. In an analogous way, we introduce the concept of the
granularity of a design. We will see that the stability of a design depends on its granularity.
Granularity Definition The granularity of the state pattern design is a measure of the
number of interfaces in the design with respect to the number of entities in its mathematical
model.
Granularity of a design compares the number of interfaces in a design to the number of
mathematical entities in its model. To clarify this idea consider the Pigeonhole principle [6],
Pigeonhole Principle given two natural numbers n and m, with n > m, if n items are put
into m pigeon holes, than at least one pigeonhole must contain more than one item.
Suppose the number of Interfaces in a design correspond to the number of pigeon holes
and the number of [mathematical] entities in a model correspond to the number of items. The
pigeonhole principle says that when the number of entities is larger than the number of
interfaces, then at least one interface must implement more than one entity. In this case, the
granularity of the design is too coarse. On the other hand, when the number of interfaces is
larger than the number of entities then the implementation of at least one interface is not
related to any entity. This interface remains closed for all changes in the model. In this case,
the granularity of the design is too fine.
For a mathematical model M, let |M| denote the number of entities of M. Since the
entities in the Graph Model are vertices and edges,
|G| = |G(V,E)| = 2.
The first design, Design 0, didn’t have any interfaces, so |Design 0| = 0. The second
design, Design 1, introduced the IState interface so |Design 1| = 1. Therefore,
|Design 0| < |G(V,E)|
and
|Design 1| < |G(V,E)|.
When the granularity of the design is too coarse, changes in the model imply
disproportionately more classes need to be re-opened and changed in the implementation. In
other words, the design is not stable. If M denotes the FSM mathematical model and D the
state pattern design of the model, then
Proposition The state pattern design is stable if and only if |D| >= |M|.
38
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
The granularity of a design concept is reminiscent of the mesh size in a numerical
algorithm. Just as the mesh size can be related to the stability of an algorithm, a design can be
refined to ensure changes in the model imply local changes in its implementation.
With this in mind let us re-design the gumball machine. As noted at the outset, the
dependencies between the state subclasses are a disadvantage of Design 1. These
dependencies arise because the design is too coarse. Recalling our mathematical models of
the FSM, we see that this design combines two mathematical entities S (the states) and δ (the
transition function) into a single interface, namely, the IState interface. According to the
Proposition such a design is not stable with respect to changes in the model. Decoupling these
two mathematical entities into their own interfaces increases the granularity of the design and
leads to a stable design of the state pattern.
The deficits of Design 1 can also be illustrated in terms of the Graph Model. This
design dictates that a vertex and edge pair be implemented in the same interface. This is not
stable since either a change in a vertex action or an edge condition would imply changing an
entire State class.
In accordance with the above discussion, an additional interface ITransition is
introduced. This interface corresponds to the transition function, δ, and defines the necessary
transitions from one state to another. As before, to handle default implementations an abstract
class Transition implementing ITransition is introduced. The implementations of the methods
in ITransition have already been worked out in the state transition table, see Table 1. As
before, all actions performed by a given state will be implemented in the derived classes of
the State class. The transition logic of changing a state, however, will be delegated to the
derived classes of ITransition. All that is necessary is that the derived State classes contain a
reference to the transition function, δ, as seen from the state transition table.
A stable design of the state pattern is given below,
Design 2 The state machine is implemented using a Context class, an interface for the
states, IState and an interface for the transitions, ITransition. The states implement IState,
and the transitions implement ITransition. The Context delegates the actions to the state
subclasses and the States delegate the transitions to the transition subclasses.
The class diagram for the new design is described in Figure 4.
For reasons of clarity only one state and one transition is shown in the class diagram.
Since the states and transitions operate on the same inputs to the state machine they have been
re-factored into the IActions interface. Design 2 has not only delegated the processing of the
states to state objects but transitions to transition objects as well. The states and the transitions
have been decoupled and localized. Just as the GumballMachine class no longer contains
actions or methods of a particular state, the states no longer contain dependencies on other
states. The class structure of the design reflects the Mathematical Model and Graph Model of
a FSM more closely. Proposition 1 is now satisfied and the design is stable.
The Elevator
The more challenging problem of modeling the operation of an elevator will
underscore the advantages of the stable design developed in the previous section. This
problem was chosen based on the results of an Internet search. The search showed that the
elevator problem was one of the most popular problems analyzed using state machine
methods. For purposes of illustration we simplify matters by considering a single elevator in a
two story building. The state diagram is shown in Figure 5.
39
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
class gumball machine design 2
«i nterface»
IActions
Gumball Machine
-
noQuarterSta te: IState
hasQuarterSt ate: IState
sol dState: IState
sol dOutState: IState
state: IState
+
+
+
+
i nsertQuart er() : voi d
ej ectQuarte r() : voi d
turnCrank() : voi d
rel easeGum ba l l () : voi d
+
+
+
+
i nsertQuarter() : voi d
ejectQuarter() : voi d
turnCrank() : voi d
di spense() : voi d
«i nterface»
ITransition
+
+
+
+
i nsertQuarter() : voi d
ejectQuarter() : voi d
turnCrank() : voi d
di spense() : voi d
«i nterface»
IState
+
+
+
+
i nsertQuarter() : voi d
ejectQuarter() : voi d
turnCrank() : voi d
di spense() : voi d
Transition
+ i nsertQuart er() : voi d
+ ej ectQuarte r() : voi d
+ turnCrank() : voi d
+ di spense() : voi d
State
+
+
+
+
i nsertQuart er() : voi d
ej ectQuarte r() : voi d
turnCrank() : voi d
di spense( ) : voi d
Trans i ti on
TransitionNoQuarter
-
gum bal l M achi ne: Gum bal l M achi ne
+
i nsertQuart er() : voi d
Sta te
NoQuarterState
-
gum bal l M achi ne: Gum bal l M achi ne
transi ti on: ITransi ti on
+
i nsertQuart er() : voi d
Figure 4: Class diagram for design 2 of the gumball machine
40
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
stm State diagram single elevator
Second Floor
Transi ti onSecondFl oor
Moving Dow n
Transi ti o nM ovi ngUp
Transi ti on M ovi ngDown
Transi ti onFi rstFl oor
First Floor
Transi ti onFi rstFl oor
Moving Up
Transi ti o nM ovi ngUp
Transi ti on M ovi ngDown
Transi sti onGroundFl oor
Ground Floor
Figure 5: State machine diagram for the single elevator
On each floor of the building there is an elevator call panel. The panel has two arrow
buttons indicating the directions Up and Down. Thus, we denote an elevator call with the
ordered pair notation [calling floor, direction]. For example, if someone is on the ground
floor and wants to go to the second floor he pushes the Up arrow button. This call is denoted
[0, Up].
Inside the elevator is the elevator command panel. This panel has buttons for the floors
and control buttons for various other elevator commands such as door open, door close,
ventilation, emergency stop, et cetera. For illustrative purposes we consider a panel only with
buttons for the floors. An elevator command is denoted by the ordered pair [start floor,
destination floor]. For example, if someone gets in on the ground floor and wants to go to the
second floor, he pushes the 2 button on the command panel and the command [0, 2] is
generated.
According to this model the elevator state objects have to process calls and commands:
thus, the interface method ProcessCallCommand was introduced. Further, when a call or
command is satisfied it has to be removed from the call-, command-stack. The removal of
calls and commands is relegated to the state classes. Since, however, the removal logic is the
same for all states it has been expressed as a helper method of the elevator class. Finally, the
stability requirement demands that the state and transition classes be defined separately. The
elevator state classes handle the functions of the elevator itself: opening and closing doors,
41
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
making an emergency stop or turning on the elevator fan. The transition classes handle all the
decisions directing the elevator where to go; in which direction and to what floor. These
aspects are shown in the class diagram below.
class
single elevator
Elev ator
-
fi rstFl oor: IState
groundFl oo r: IState
m ovi ngDown : IState
m ovi ngUp: IState
secondFl oor: IState
State: IState
Fl oor: i nt
Desti nati on: i nt
Door: Doo rPosi ti on
Control l er: Control l erType
«i nterface»
IState
+
ProcessCal l Command() : voi d
+ ProcessCal l Com m and() : voi d
+ Rem oveCal l Com m and() : voi d
MovingDow n
-
SecondFloor
transi ti on: ITransi ti on
el evator: El evator
-
+ ProcessCal l Com m and() : voi d
Movi ngUp
transi ti on: ITransi ti on
el evator: El evator
-
+ ProcessCal l Com m and() : voi d
transi ti on: ITransi ti on
el evator: El evator
+ ProcessCal l Com m and() : voi d
FirstFloor
-
transi ti on: ITransi ti on
el evator: El evator
+ ProcessCal l Com m and() : voi d
GroundFloor
-
«i nterface»
ITransition
transi ti on: ITransi ti on
el evator: El evator
+
ProcessCal l Command() : voi d
+ ProcessCal l Com m and() : voi d
TransitionMovingDow n
-
el evator: El evator
TransitionSecondFloor
-
+ ProcessCal l Com m and() : voi d
+ ProcessCal l Com m and() : voi d
TransitionGroundFloor
-
el evator: El evator
-
el evator: El evator
+ ProcessCal l Com m and() : voi d
TransitionFirstFloor
-
+ ProcessCal l Com m and() : voi d
el evator: El evator
TransitionMovingUp
el evator: El evator
+ ProcessCal l Com m and() : voi d
Figure 6: Class diagram for the elevator
For purposes of clarity only a representative set of connectors is shown in the class
diagram.
42
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
The set of all possible calls for the elevator problem is, calls = {[0, Up], [1, Up], [1,
Down], [2, Down]}. The set of all possible commands is, commands = {[0, 1], [0, 2], [1, 2],
[1, 0], [2, 1], [2, 0]}. Invalid calls and commands such as [0, Down] have been omitted.
Denote s0 = GroundFloor, s1 = FirstFloor, s2 = SecondFloor, s3 = MovingUp and s4 =
MovingDown. Then, the state transition table for the elevator is
s
3
s
1
s
s
0
4
s
s
4
[2, 0]
[2, 1]
[1, 0]
[1, 2]
s
3
s
4
s
s
4
4
s
3
s
3
3
s
4
s
3
s
s
4
2
s
3
[0, 2]
[1,
Down]
[2,
Down]
[0, 1]
[1, Up]
[0, Up]
Table 2: State transition table for the elevator
s
s
1
2
s
4
s
2
s
4
s
0
s
1
s
0
This table helps with the implementation of the transition logic for the elevator state
machine.
For the first implementation of the elevator we make the simplifying assumption that each
command is carried out to its end before subsequent calls are considered. This means, for
example, that if someone has entered the elevator on the ground floor and enters a command
to go the second floor, command = [0, 2], then even if a person makes a call on first floor
before the elevator reaches the first floor, call = [1, Up], the elevator will carry out the
command to its end and continue on the second floor before processing the call from the first
floor. In the next section, when examining the stability of the design, this assumption will be
removed.
First, the transition logic in the ProcessCallCommand method for the transition class
TransitionGroundFloor is straightforward,
public void ProcessCallCommand(Collection<Call>
commands)
{
if (commands.Count > 0) // evaluate command
{
Command command = commands[0];
if (command.endFloor > 0)
{
elevator.State = elevator.MovingUp;
elevator.Destination = command.endFloor;
elevator.Door = DoorPosition.Closed;
}
}
calls,
Collection<Command>
43
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
else if (calls.Count > 0) // evaluate call
{
Call call = calls[0];
if (call.Floor > elevator.Floor)
{
elevator.Destination = call.Floor;
elevator.State = elevator.MovingUp;
elevator.Door = DoorPosition.Closed;
}
else if (call.Floor < elevator.Floor)
{
// error
}
}
}
For our discussion the following scenario with 3 Persons in a two-story building will
be used. Originally Person 1 is on the ground floor, Person 2 is on the first floor and Person 3
is on the second floor.
Figure 7: Output for the elevator
44
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
Scenario The elevator starts on the ground floor and at time, t=0, Person 1, makes the call,
call = [0, Up]. At the next time slot, t=1, Person 1 enters the command, command = [0, 2] and
simultaneously Person 2 makes the call, call = [1, Up]. At time slot, t=6, Person 2 enters the
command, command = [1, 2] and simultaneously Person 3 makes the call, call = [2, Down].
At time slot 8, Person 3 enters the command, command = [2, 0].
The assumption is made that the elevator needs 1 second to travel from one floor to
another. This assumption is neutral in that it doesn’t affect the complexity or relevance of the
model. The implementation generates the output in Figure 7.
After receiving the call to the ground floor, the elevator responds to the first command and
delivers Person 1 to the second floor. Then the elevator responds to the call of Person 2 on the
first floor by going from the second floor to the first floor. The command of Person 2 to go to
the second floor causes the elevator to go to the second floor for the second time. There,
Person 2 disembarks and the call of Person 3 is satisfied. Lastly, the command of Person 3 is
satisfied as the elevator returns to the ground floor.
stm State diagram gumball machine w inner
i nsertQuarter
noQuarterState
hasQuarterState
ej ectQuarter
di spense [N > 0]
turnCrank [no Wi nner]
soldState
di spense 2 gum bal l s
[N > 0]
di sp ense
[N = 0]
turnCrank [Wi nner]
SoldOutState
di spense 2 gum bal l s
[N = 0]
WinnerState
Figure 8: State diagram for gumball machine with winner state
45
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
Stability of the Designs
To test the stability of the new state pattern design we make a change to the model and
examine how it affects the implementation. First we return to the gumball machine and
consider the following change [1],
Change The gumball machine is changed so that 10% of the time when the crank is turned
the customer gets an additional gumball.
This change adds a new state and new transition functions to the gumball machine. The
updated state diagram is in Figure 8.
Let us consider the changes to the implementation of Design 1 as a result of this change.
First, a new state is introduced: the Winner state. The gumball machine enters this state 10%
of the time and releases an extra gumball to the customer. The class WinnerState is a child of
the State class and implements the IState interface. Accordingly, a new reference to the
WinnerState object needs to be added to the Context class, GumballMachine. Finally, the
HasQuarterState needs to be changed to initiate the WinnerState as seen in Figure 8. This
change occurs in the turnCrank method and is shown in the following code snippet
public override void turnCrank()
{
Console.WriteLine("You turned...");
int winner = random.Next(10);
if ((winner == 0) && (gumballMachine.Count > 1))
{
gumballMachine.State = gumballMachine.WinnerState;
}
else
{
gumballMachine.State = gumballMachine.SoldState;
}
}
The changes to the implementation of Design 2 as a result of this change are as follows.
As before a new “Winner” state needs to be introduced and its class WinnerState must
implement the IState interface. And, of course, a reference to the WinnerState must be added
to GumballMachine. Since the transition logic of the states has been removed from the state
classes, the remaining state classes are “closed” and need not be changed. Instead, the
TransitionHasQuarter class needs to be updated to initiate the WinnerState.
With regard to this change in the gumball machine the implementations of Design 1 and
Design 2 are comparable. The difference is that while in Design 1 a change was made to an
existing state class; in Design 2 a new Transition class was added. This is not surprising when
considering the state machine diagram as a mathematical graph. This change demanded a new
vertex and new edges be defined for the multidigraph. When a change demands adding a new
state, there is no getting around adding a new state class and new state transition logic. In
Design 2 the turnCrank method in the HasQuarterState is
public override void turnCrank()
46
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
{
Console.WriteLine("You turned...");
transition.turnCrank(this.gumballMachine);
}
Although this change is rather long, Design 2 insures that only one transition behavior of
the gumball state machine is affected. The focusing nature of the design seen here is a
characteristic of its stability. As previously remarked, this reduces testing and increases
readability and understandability. The stability of Design 2 becomes apparent for all state
machine changes consisting of state changes, or actions within a state, or when the transition
logic between states is changed.
Now we examine the single elevator problem and consider a change to the elevator
model. In the first implementation of the elevator we made the simplifying assumption that
each command was carried out to its end before subsequent calls were considered. To
examine the stability of the design for the elevator model we relax this assumption and
consider what changes are necessary in the implementation.
Change The carrying out of Commands may be interrupted to satisfy Calls provided they
do not cause a change of direction of the elevator before reaching the Command’s
destination.
For example, suppose as before the elevator is on the ground floor and receives a
Command to go to the second floor. Before the elevator reaches the first floor a Call is made
from the first floor to go Up. This call will cause the elevator to stop on the first floor before
completing the command of going to the second floor. This corresponds to the standard
behavior of an elevator.
The necessary changes are 1) updating the process logic of the TransitionMovingUp and
TransitionMovingDown classes and 2) updating the remove logic in the
RemoveCallCommand method. The following code snippet shows the change in the
TransitionMovingUp logic,
public void ProcessCallCommand(Collection<Call>
commands)
{
if (elevator.Controller == ControllerType.PickUp)
{
// pick up on the way modification
foreach (Call call in calls)
{
if ((call.Floor == elevator.Floor) &&
(call.Direction == Direction.Up))
{
if (elevator.Floor == 1)
{
elevator.State = elevator.FirstFloor;
elevator.Destination = elevator.Floor;
}
break;
}
calls,
Collection<Command>
47
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
}
}
In other words, all the calls are examined to see if there is one for the current floor with
the same direction as the elevators. If so, the elevator’s destination is updated so that the
elevator stops on the floor directed by the call. The second change in the logic of removing
calls and commands is that instead of only removing the most recent call and command, all of
the calls and commands to the elevator’s current floor are removed from the call- and
command-stack.
Re-executing the same scenario leads to the following result,
Figure 9: Output for the single elevator
In comparison with the previous result the following three conclusions are evident. First,
the elevator stops on the first floor before continuing on to satisfy the command on the second
floor. Second, when reaching the second floor all relevant commands and calls are removed.
And finally, the time it takes the elevator to complete the scenario has decreased.
The implementation of this change required neither design nor class structural
changes. The state machine diagram and class diagram remained valid. Although the change
to the elevator model was complex, all implementation changes were “localized” or
“pigeonholed”. This is precisely what is expected from a stable design.
48
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
Conclusions
Mathematical models have been used to motivate a stable design of the state pattern. In
addition to the notion of stability, criteria have been developed for determining when a design
is stable. Finally, these ideas have been illustrated on two well-known examples in the OO
literature.
References
[1] E. Freeman and E. Freeman. Head First Design Patterns. O’Reilly, Sebastopol, CA,
2004.
[2] E. Gamma, R. Helm, R.Johnson, and J. Vlissides. Design Patterns: Elements of Reusable
Object-Oriented Software. Addison-Wesley, Reading, MA, 1995.
[3] http://en.wikipedia.org/wiki/Finite_state_machine, Wikipedia, 2009.
[4] http://en.wikipedia.org/wiki/Graph_theory, Wikipedia, 2009.
[5] http://en.wikipedia.org/wiki/Von_Neumann_stability_analysis, Wikipedia, 2009.
[6] http://en.wikipedia.org/wiki/Pigeonhole_principle, Wikipedia, 2009.
Author
Dr. Gore, a native of White Plains, NY, has been working as a
software engineering consultant in the Munich area for the past 5
years. He received his Ph.D. from Rensselaer Polytechnic Institute
in applied mathematics. Before re-locating to Germany he was an
assistant professor of mathematics at Louisiana State University.
His areas of interest are in software design, mathematical modelling
and inverse problems.
49
International Journal of Software Engineering and Its Applications
Vol. 3, No.4, October 2009
50