State pattern

From Wikipedia, the free encyclopedia
Jump to: navigation, search

The state pattern, which closely resembles Strategy Pattern, is a behavioral software design pattern, also known as the objects for states pattern. This pattern is used in computer programming to encapsulate varying behavior for the same routine based on an object's state object. This can be a cleaner way for an object to change its behavior at runtime without resorting to large monolithic conditional statements[1]:395.

Contents

Structure [edit]

State in UML[1][2]
State in LePUS3[2][3]

Example [edit]

Pseudocode [edit]

Take, for example, a drawing program. The program has a mouse cursor, which at any point in time can act as one of several tools. Instead of switching between multiple cursor objects, the cursor maintains an internal state representing the tool currently in use. When a tool-dependent method is called (say, as a result of a mouse click), the method call is passed on to the cursor's state.

Each tool corresponds to a state. The shared abstract state class is AbstractTool:

 class AbstractTool is
     function moveTo(point) is
         input:  the location point the mouse moved to
         (this function must be implemented by subclasses)
 
     function mouseDown(point) is
         input:  the location point the mouse is at
         (this function must be implemented by subclasses)
 
     function mouseUp(point) is
         input:  the location point the mouse is at
         (this function must be implemented by subclasses)

According to this definition, each tool must handle movement of the mouse cursor and also the start and end of any click or drag.

Using that base class, simple pen and selection tools could look like this:

 subclass PenTool of AbstractTool is
     last_mouse_position := invalid
     mouse_button := up
 
     function moveTo(point) is
         input:  the location point the mouse moved to
         if mouse_button = down
             (draw a line from the last_mouse_position to point)
             last_mouse_position := point
 
     function mouseDown(point) is
         input:  the location point the mouse is at
         mouse_button := down
         last_mouse_position := point
 
     function mouseUp(point) is
         input:  the location point the mouse is at
         mouse_button := up  
 subclass SelectionTool of AbstractTool is
     selection_start := invalid
     mouse_button := up
 
     function moveTo(point) is
         input:  the location point the mouse moved to
         if mouse_button = down
             (select the rectangle between selection_start and point)
 
     function mouseDown(point) is
         input:  the location point the mouse is at
         mouse_button := down
         selection_start := point
 
     function mouseUp(point) is
         input:  the location point the mouse is at
         mouse_button := up

For this example, the class for the context is called Cursor. The methods named in the abstract state class (AbstractTool in this case) are also implemented in the context. In the context class, these methods invoke the corresponding method of the current state, represented by current_tool.

 class Cursor is
     current_tool := new PenTool
 
     function moveTo(point) is
         input:  the location point the mouse moved to
         current_tool.moveTo(point)
 
     function mouseDown(point) is
         input:  the location point the mouse is at
         current_tool.mouseDown(point)
 
     function mouseUp(point) is
         input:  the location point the mouse is at
         current_tool.mouseUp(point)
 
     function usePenTool() is
         current_tool := new PenTool
 
     function useSelectionTool() is
         current_tool := new SelectionTool

Notice how one Cursor object can act both as a PenTool and a SelectionTool at different points, by passing the appropriate method calls on to whichever tool is active. That is the essence of the state pattern. In this case, we could have combined state and object by creating PenCursor and SelectCursor classes, thus reducing the solution to simple inheritance, but in practice, Cursor may carry data that is expensive or inelegant to copy to a new object whenever a new tool is selected.

Java [edit]

The state interface and two implementations. The state's method has a reference to the context object and is able to change its state.

interface Statelike {
 
    /**
     * Writer method for the state name.
     * @param STATE_CONTEXT
     * @param NAME
     */
    void writeName(final StateContext STATE_CONTEXT, final String NAME);
 
}
 
class StateA implements Statelike {
    /* (non-Javadoc)
     * @see state.Statelike#writeName(state.StateContext, java.lang.String)
     */
    @Override
    public void writeName(final StateContext STATE_CONTEXT, final String NAME) {
        System.out.println(NAME.toLowerCase());
        STATE_CONTEXT.setState(new StateB());
    }
 
}
 
class StateB implements Statelike {
    /** State counter */
    private int count = 0;
 
    /* (non-Javadoc)
     * @see state.Statelike#writeName(state.StateContext, java.lang.String)
     */
    @Override
    public void writeName(final StateContext STATE_CONTEXT, final String NAME) {
        System.out.println(NAME.toUpperCase());
        // Change state after StateB's writeName() gets invoked twice
        if(++count > 1) {
            STATE_CONTEXT.setState(new StateA());
        }
    }
 
}

The context class has a state variable that it instantiates in an initial state, in this case StateA. In its method, it uses the corresponding methods of the state object.

public class StateContext {
    private Statelike myState;
        /**
         * Standard constructor
         */
    public StateContext() {
        setState(new StateA());
    }
 
        /**
         * Setter method for the state.
         * Normally only called by classes implementing the State interface.
         * @param NEW_STATE
         */
    public void setState(final Statelike NEW_STATE) {
        myState = NEW_STATE;
    }
 
        /**
         * Writer method
         * @param NAME
         */
    public void writeName(final String NAME) {
        myState.writeName(this, NAME);
    }
}

The test below shows also the usage:

public class TestClientState {
    public static void main(String[] args) {
        final StateContext SC = new StateContext();
 
        SC.writeName("Monday");
        SC.writeName("Tuesday");
        SC.writeName("Wednesday");
        SC.writeName("Thursday");
        SC.writeName("Friday");
        SC.writeName("Saturday");
        SC.writeName("Sunday");
    }
}

According to the above code, the output of main() from TestClientState should be:

monday
TUESDAY
WEDNESDAY
thursday
FRIDAY
SATURDAY
sunday

See also [edit]

References [edit]

External links [edit]