State Pattern: State Machines • A state machine (finite state machine

State Pattern: State Machines
• A state machine (finite state machine, finite automaton, ...) is an abstraction
used to model the behavior of a system
• A machine is characterized by
1. A set of states
2. A set of transitions between states
• Transitions are based on input to the system
• (Aside: Formally, a Deterministic Finite Automaton (DFA) M is defined as
M = (K, Σ, δ, s, A), where
K is a finite set of states
Σ is an input alphabet
s ∈ K is a distinguished state called the start state
A ⊆ K is a set of accepting states
δ is a transition function, mapping K × Σ → K
)
• An FA can be represented as a directed graph
– Nodes of the graph represent the states
– Arcs of the graph represent the transitions
– Each arc is labeled with the input that triggers the transition
• Example:
• If additional info is stored/used at a node, the FA is sometimes refered to as
an augmented FA
1
State Pattern: State Machines (2)
• To implement an FA:
1. Id states
2. Id the legal inputs (actions)
3. Create a class for the machine
(a) Create an instance variable for the current state
(b) Create a method for each action
– Each method consists of a conditional that checks current state and
determines what to do based on the action
2
State Pattern: State Machines (3)
• Sample code:
public class FA {
final static enum(STATE0, STATE1, STATE2, ...);
int currentState = STATE0;
public FA () {
//any iniialization needed
}
public void action1 () {
if (currentState == STATE0) {
currentState = //new state;
//process as needed
}
else if (currentState == STATE1) {
currentState = //new state;
//process as needed
}
else if (currentState == STATE2) {
currentState = //new state;
//process as needed
}
...
}
public void action2 () {
if (currentState == STATE0) {
currentState = //new state;
//process as needed
}
else if (currentState == STATE1) {
currentState = //new state;
//process as needed
}
else if (currentState == STATE2) {
currentState = //new state;
//process as needed
}
...
}
...
}
public class Driver {
static public void main (String[] args) {
machine = new FA();
machine.action2();
machine.action5();
macihne.action1();
...
}
...
}
3
State Pattern: State Machines (4)
• Note: The text example has a number of cases where nothing appears to happen
on an action, other than a msg stating that effectively the action isn’t applicable
to the current state
– This is an implicit transition from a state to itself
– One could argue that such a situation should be made explicit
• For a given state, there should be one transition for each action that can be
performed on the machine
• Issues with this approach:
1. To extend a machine (by adding a new state), will need to add a new state
variable
2. This would require adding an extra clause to the conditional of every action
4
State Pattern: State Pattern Approach
• To remedy the initial approach, use a class for each state
– This follows the principle of encapsulate what varies
– Each state will be responsible for its behavior WRT each action
– This will make adding new states transparent to existing states
– This will also promote easy modification of a state’s behavior
• To implement this:
1. Create a State interface
– This interface contains a signature for each action that can be performed
by the machine
2. Create a class for each state
– Each state implements each action appropriately for the state
– Each also has an instance variable to represent the machine object with
which it is associated
3. Modify the state machine
(a) Instance variables representing states are now State objects instead of
ints
(b) These are initialized by the constructor
(c) Action methods now call the associated action of the current state
5
State Pattern: State Pattern Approach (2)
• Class diagram:
6
State Pattern: State Pattern Approach (3)
• Sample code:
public interface State {
public void action1 ();
public void action2 ();
...
}
public State0 {
private FA machine;
public State0 (FA machine) {
this.machine = machine;
}
public void action1 () {
machine.setState(machine.get//new state;
//process as needed
}
public void action2 () {
machine.setState(machine.get//new state;
//process as needed
}
...
}
public State1 {
private FA machine;
public State0 (FA machine) {
this.machine = machine;
}
public void action1 () {
machine.setState(machine.get//new state;
//process as needed
}
public void action2 () {
machine.setState(machine.get//new state;
//process as needed
}
...
}
7
State Pattern: State Pattern Approach (4)
public class FA (
State state0;
State state1;
State state2;
...
State currentState = state0;
//start state
public class FA () {
state0 = new State0(this);
state1 = new State1(this);
state2 = new State2(this);
...
}
public void action1 () {
currentState.action1();
}
public void action2 () {
currentState.action2();
}
public void action3 () {
currentState.action3();
}
...
}
public class Driver {
static public void main (String[] args) {
machine = new FA();
machine.action2();
machine.action5();
macihne.action1();
...
}
...
}
8
State Pattern: State Pattern Approach (5)
• Key points to note:
1. Behavior of a state is encapsulated within the class for that state
2. Conditionals have been eliminated
3. States are closed for modification, but the FA is open for extension
4. Design more closely resembles a state diagram
9
State Pattern: Defined
• State Pattern:
Allows an object to alter its behavior when its internal state changes.
The object will appear to change its class.
– The first part is straightforward
– The second part means that, since an object can completely change its
behavior, from the client’s perspective it appears to be an object from
another class
• Class diagram:
10
State Pattern: Points to Note
• Class diagram essentially the same as the Strategy Pattern
– What differs is the intent of the two patterns
1. State
∗ Behavior encapsulated in State objects
∗ Context is in a given state at any one time
∗ Current state changes over time, and is reflected in changes of behavior
∗ State objects invisible to client
∗ Provides a flexible alternative to conditionals
∗ Behavior changes by changing state in Context
2. Strategy
∗ Client usually responsible for Strategy object Context is composed
with
∗ While this object can be changed at runtime, it usually isn’t because
it was chosen as most appropriate for the Context
∗ Provides a flexible alternative to subclassing
∗ Behavior changes by composing with a different object
• Instead of encapsulating transitions in the state classes (as above), put them
in Context
– If transitions are fixed, Context more appropriate
– If more dynamic (dependent on a state variable), state classe more appropriate
– Disadvantage of using state classes is that it creates interdepencies among
them
– The choice affects which classes are open for modification
• Clients never interact directly with state classes
• State objects can be shared among several instances of Context
– State objects cannot keep their own internal state
– Usually use a unique static instance variable for each state
– Will need to give each state a reference to a Context in each handler if it
needs to access Context
11
State Pattern: Points to Note (2)
• Promotes large number of classes
– Alternative is conditionals, making code more difficult to read and maintain
• Can use abstract class (as in class diagram) as opposed to interface (as in
example)
– Depends on situation
12