State Design Pattern in Java

This article explains State design pattern in java with UML class diagram. It then takes an example scenario in java and explains it with class diagram and code.

Introduction: State Design Pattern is a behavioral design pattern among the Gang Of Four(GOF) Design PatternsArticle on GOF Patterns & their types. Being a behavioral design pattern, the State pattern deals with how the objects communicate and share responsibilities among each other.

What is State Design Pattern
State Design Pattern allows the behavior of an object to vary based on its state. I.e. whenever the object’s state changes, its behavior changes as per its new state. To the observer it appears as if the object has changed its class.

Important advantage of State Design Pattern
If there are state-based scenarios in a system where the different states of an object are implemented via multiple sets of conditional statements. And each set of conditional statements is applied based on the condition that the object is in the state corresponding to those statements. In such scenarios, instead of writing multiple if-else blocks for each of the states and juggling with the various state dependent values, it becomes much easier to encapsulate each of these set of statements into separate classes of their own. The implementation of each state can thus vary independently of the other states.

Class Diagram for State Design Pattern

State Design Pattern Class Diagram

Explanation of State Design Pattern’s Class Diagram

  • State is the base class for all the child ConcreteState classes.
  • The Context class holds a reference to the State base class in an attribute named state. In this attribute Context stores the object of specific ConcreteState implementation corresponding to the state in which the Context is currently.
  • Client invokes a request on the Context class using its request() method.
  • When the handle() method is invoked on the base state reference stored in the Context then the overloaded handle() method of the current ConcreteState instance stored in it is executed.
  • Hence, the states show different behavior for the same handle() method invoked on the Context based on the current state the system/context is in.

State Design Pattern in Java – Class Diagram:

State Design Pattern in Java Class Diagram
Code for the classes shown in Java Example’s Class Diagram
public interface State{
 public void assignToDev();
 public void assignToTester();
 public void markTested();
}
public class ChangeRequestContext {
  State newState;
  State underDevState;
  State underTestState;
  State closedState;
  State currentState = new NewState(this);
  public ChangeRequestContext() {
    newState = new NewState(this);
    underDevState = new UnderDevState(this);
    underTestState = new UnderTestState(this);
    closedState = new ClosedState(this);
  }
  public void assignToDev() {
    currentState.assignToDev();
  }
  public void assignToTester() {
    currentState.assignToTester();
  }
  public void markTested() {
    currentState.markTested();
  }
  //setters and getters for the 4 States' attribute & the currentState attribute
}
public class NewState implements State {
  private ChangeRequestContext CRSystem;
  public NewState(ChangeRequestContext CRSystem) {
    this.CRSystem = CRSystem;
  }
  public void assignToDev() {
    System.out.println("Assigning to available developer.");
   this.CRSystem.setCurrentState(CRSystem.getUnderDevState());
  }
  public void assignToTester() {
    System.out.println("Cannot be assigned to tester from new state.");
  }
  public void markTested() {
   System.out.println("Cannot be marked tested when it is new.");
  }
}
public class UnderDevState implements State{
  private ChangeRequestContext CRSystem;
  public UnderDevState(ChangeRequestContext CRSystem){
    this.CRSystem=CRSystem;
  }  
  public void assignToDev(){
    System.out.println("It's already assigned to a developer.");
  }  
  public void assignToTester(){
    System.out.println("Assigning to available tester.");
    this.CRSystem.setCurrentState(CRSystem.getUnderTestState());
  }  
  public void markTested(){
	  System.out.println("Cannot be marked tested when it is under dev.");  	  
  }
}
public class UnderTestState implements State{
  private ChangeRequestContext CRSystem;
  public UnderTestState(ChangeRequestContext CRSystem) {
    this.CRSystem = CRSystem;
  }
  public void assignToDev() {
    System.out.println("Assigning Back to Developer.");
    this.CRSystem.setCurrentState(CRSystem.getUnderDevState());
  }
  public void assignToTester() {
     System.out.println("Cannot be done as already under test.");
  }
  public void markTested() {
    System.out.println("Marking as tested.");
 	this.CRSystem.setCurrentState(CRSystem.getClosedState());
  }
}
public class ClosedState implements State{
  private ChangeRequestContext CRSystem;
  public ClosedState(ChangeRequestContext CRSystem) {
    this.CRSystem = CRSystem;
  }
  public void assignToDev() {
    System.out.println("Cannot be done as CR is closed.");
  }
  public void assignToTester() {
    System.out.println("Cannot be done as CR is closed.");
  }
  public void markTested() {
    System.out.println("Cannot be done as CR is closed.");
  }
}
public class Client {
  public static void main(String args[]){
    ChangeRequestContext CRSystem=new ChangeRequestContext();
    CRSystem.assignToTester();
    CRSystem.assignToDev();
    CRSystem.markTested();
    CRSystem.assignToTester();
    CRSystem.assignToDev();
    CRSystem.assignToTester();
    CRSystem.markTested();
  }
}
 OUTPUT on running Client.java
Cannot be assigned to tester from new state.
Assigning to available developer.
Cannot be marked tested when it is under dev.
Assigning to available tester.
Assigning Back to Developer.
Assigning to available tester.
Marking as tested.
Explanation of Java Example’s Class Diagram & Code
The Java class diagram above depicts State Design pattern implemented for a ChangeRequest State Handler. Lets quickly go through whats there in UML class diagram & corresponding code –

  • State is the base interface which implements 3 methods – assignToDev, assignToTester() and markTested(). These 3 methods represent the 3 transitions possible between states.
  • There are 4 concrete states – NewState, UnderDevState, UnderTestState and ClosedState.
  • The allowed transitions between states are –
    • NewState to UnderDevState transition is possible through assignToDev().
    • UnderDev state to UnderTest state transition is possible through assignToTester().
    • In UnderTest state tester can transition the CR back to UnderDev state if he finds an error using assignToDev(). If tester passes the CR then he transitions it to Closed state using markClosed().
  • The Client invokes series of transitions in sequence. Some transitions are valid and move the CR to the next state. For eg: When moving from New to UnderDev the following is output – Assigning to available developer.
  • There are also some invalid transitions such as invoking assignToTester() in NewState without going through UnderDev state in which case the line Cannot be assigned to tester from new state – is printed.
  • Thus, a CR moves through the states – New > UnderDev > UnderTest > Closed during its lifecycle.

Summary
In the above tutorial we understood what is State design pattern and its main advantage. We then looked at the UML class diagram for State Design Pattern & its explanation, a Java Use Case implementing State pattern with its class diagram and code for the classes shown in the class diagram, followed by explanation of both the class diagram & code. This concludes the tutorial on State design pattern.

 

Digiprove sealCopyright © 2014-2016 JavaBrahman.com, all rights reserved.
  • Lars

    Thanks, this tutorial on the State pattern is helpful. For one thing, the ChangeRequest example is a familiar and relevant concept, simple enough to understand easily, yet rich enough to demonstrate the issues involved. In particular, you demonstrated the use of true delegation via the passed CRSystem parameter.

    I think to avoid confusion, you may want to clarify “These 3 methods represent the 3 transitions possible between states.” Since assignToDev() can represent a transition either from the UnderTestState or from the NewState, it seems there are actually 4 transitions possible.

    • Hi Lars,

      Thanks for pointing this out. The transition where UnderTestState assigns the change request back to developer is a different transition altogether. I will rename it to reAssignToDev().There are indeed 4 transitions in all.

      Regards,
      Dhruv