Sunday, November 2, 2014

State Design Pattern

Before exam only study; after exam only play !

State pattern is a behavioral design pattern which enables an object to change its behavior when its state changes. The state transitions is encapsulated so client only sees it as the side effect in the form of changed behavior. So whenever the state changes, the state reference starts pointing to a different state object.

Intent(as put in Gang of four book)
"Allows an object to alter its behavior when its internal state changes. The object will appear to change its class. "

Implementation  

Let's briefly cover components or actors of this pattern.

Context: Interface for the client. Maintains instance of the current State (shown below in class diagram as aggregation relationship). Delegates state-specific request to a particular instance.
State: Declares an interface common to all subclasses. Subclasses provide different operational states.
ConcreteState: Implements a specific behavior

Who does state transition/switching ?
There are three players in this pattern - Context, State and ConcreteState. State provides interface for operations (it's usually implemented as either abstract class or as an interface in Java). This leaves with Context or ConcreateState. The pattern doesn't mandate which component should do state transition. So either Context or the ConcreateState can decide which state succeeds another and under what conditions.

Class diagram (from Gang of four book)


package state.pattern;

/**
 * Context : Encapsulates behavior as well as state transition
 * 
 * @author Siddheshwar
 * 
 */
public class Student {
 private StudyState state;

 public Student() {
  this.state = new AfterExam(); // default state
 }

 /**
  * client calls this generic operation always
  */
 public void performAction() {
  state.study(this);
 }

 /**
  * called by implementing states
  */
 public void changeState(StudyState state) {
  this.state = state;
 }
}

package state.pattern;

/**
 * State : Provides base behavior interface
 *
 */
public interface StudyState {
 public void study(Student student);
}

package state.pattern;

/**
 * Concrete state 1 
 *
 */
public class BeforeExam implements StudyState {

 @Override
 public void study(Student student) {
  System.out.println(" Exam time; need to study hard !");

  //switch state
  student.changeState(new AfterExam());
 }
}

package state.pattern;

/**
 * Concrete State 2
 *
 */
public class AfterExam implements StudyState {

 @Override
 public void study(Student student) {
  System.out.println(" Semester has just begun; play and party time !");
  
  //switch state
  student.changeState(new BeforeExam());
 }
}

//sample client
public static void main(String[] args) {
  Student s = new Student();
  s.performAction();
  s.performAction();
  
  s.performAction();
  s.performAction();
 }

Output:
 Semester just begun; play and party time !
 Exam time; need to study hard !
 Semester just begun; play and party time !
 Exam time; need to study hard !


Client always calls the same operation but behavior changes; evident from the above output. In above case state gets updated by concrete state sub classes. This is the reason for passing the Context instance (i.e. Student) in the behavioral method (i.e. study(..)).
State can be modified/updated in the Context class as well. So context can update the state using changeState(..) method based on some condition.

Notes

  • You should use this pattern when the behavior of an object is influenced by its internal state.  
  • Avoids inconsistent states as the state change gets controlled properly
  • State objects can be implemented as Singletons

References:
http://userpages.umbc.edu/~tarr/dp/lectures/StateStrategy.pdf

No comments:

Post a Comment