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
© Copyright 2024