Skip to main content

Implementing the State Design Pattern using Enums

Posted by ryano on January 31, 2005 at 4:14 PM PST

Recently I've been reading "Head First Design Patterns" as well as
"Java 1.5 Tiger - A Developer's Notebook". Both are really good
books by the way.  I was reading the chapter on the State design
pattern when it occurred to me that the new Enum feature in J2SE 5.0
would be a perfect way to implement the state design pattern.



Typically, the state pattern is used to model a state transition graph allowing an object to modify its
behavior as the state of the graph changes.  The pattern
typically defines a "state" interface containing a method declaration for
each state transition in the graph.  Next, a class is created
for each state in the state graph.  These state classes implement
the "state" interface.  Clients hold a reference to the current
state which it treats as a "state" interface
type.  As the client invokes methods on the current state,
the concrete state object performs some operation and updates the
current state in the client.  This decouples the client from the state management details.



A simple example of a state machine is a light switch.  The switch
has two states, on and off.  The switch also has two transitions,
turnOn and turnOff.  If the switch is in the off state and
receives a command to turnOn the switch transitions to the on
state.  If the switch is in the off state and is told to turn off,
the switch does nothing since it is already off.  If the switch is
in the on state and is told to turnOff the switch transitions to the
off state.  Finally, if the switch is in the on state and is told
to
turn on the switch does nothing.



Following the typical state pattern implementation, the "state"
interface would contain two methods, turnOn and turnOff.  Two
classes would be created to represent the two states, on and off. 
Each class would implement the "state" interface.  The client
would hold a reference to an object of the "state" interface type and call the
appropriate methods on this object as events occur.



Using the Enum construct in J2SE 5.0, the entire state pattern can be
contained in an enumerated type definition.  The abstract methods
declared in the enumerated type take the place of the "state" interface
and each enumeral implements the abstract methods much like the
concrete state classes implement the "state" interface.



The following enumerated type encapsulates the switch states and
transitions:

enum State {
    ON(1) {
      public State turnOn() {
        System.err.println("Already ON");
        return ON;
      }
      public State turnOff() {
        System.err.println("Turning OFF");
        return OFF;
      }
    },
    OFF(0) {
      public State turnOn() {
        System.err.println("Turning ON");
        return ON;
      }
      public State turnOff() {
        System.err.println("Already OFF");
        return OFF;
      }
    };

    private int val;
    State(int val) {
      this.val = val;
    }

    public abstract State turnOn();
    public abstract State turnOff();
}

Here is a simple Driver that exercises the Enumerated type:

public class Driver {
    private State state = State.OFF;
    public void go() {
      System.out.print("turnOn()\t");  state = state.turnOn();
     System.out.print("turnOn()\t");  state = state.turnOn();
      System.out.print("turnOff()\t"); state = state.turnOff();
     System.out.print("turnOff()\t"); state = state.turnOff();
      System.out.print("turnOn()\t");  state = state.turnOn();
      System.out.print("turnOn()\t");  state = state.turnOn();

    }
    public static void main(String[] args) {
      new Driver().go();
    }
}



Each enumeral represents a state and each abstract method represents a
state transition.  Each enumeral implements the state
transitions
and acts accordingly.  As new states and transitions are
introduced the
user can simply update this enumerated type.  This seems like an
interesting way to implement the state design pattern. It also localizes the changes to one source file (excluding the Client). In the original version of the pattern the "state" interface would have to be updated with new transition methods. Each state class that implements the interface would need to add implementations of the new methods and for each new state a new class would be created.

Related Topics >>