The State Pattern is a behavioral design pattern which allows an object to alter its behavior when its internal state changes. The object will appear to change its class.
In state pattern we encapsulate the state into separate classes and delegate the work to be done to the object representing the current state and behaviour changes along with the internal state which will be represented by the current state. Over time the current state changes across the set of state objects to reflect the internal state of the context, so the context behaviour changes over time as well. The client usually knows very little about the state objects.
This pattern is similar to the strategy design pattern but the intent of the two patterns are different. With Strategy, the client usually specifies the strategy object that the context is composed with. Now, while the pattern provides the flexibility to change the strategy object at runtime, often there is a strategy object that is most appropriate for a context object.
In general Strategy Pattern is a flexible alternative to subclassing. if you use inheritance to define the behaviour of a class, then you’re stuck with that behaviour even if you need to change it. With Strategy you can change the behaviour by composing with a different object. And State Pattern is an alternative to putting lots of conditionals in your context by encapsulating the behaviours within state objects, you can simply change the state object in context to change its behaviour.
Below is the generic state class diagram
Lets take an example of a traffic signalling system which deals with the conditions as below
1. Green Light- It represents Move state of the vehicles.
2. Yellow Light- It warns that signal is about to change from green to red or red to green.
3. Red Light- It represents Stop state of the vehicles.
Thus, here object changes its behaviour at run time and if we need to code this without using many conditional statements then state pattern is a good choice as this pattern allows an object to alter its behaviour, when its internal state changes. Its state changes from Green, Yellow to Red and red to yellow and to green and based on this, traffic behaviour also changes.
Lets code this example.
Below is the class diagram we will be building
We will start with the state interface which has two methods or actions changestate and displaystate which has the functionality to change the state and also to display the current state.
public interface State { public void changeState(); public void displayState(); }
Lets code the concrete states .
We have three states green,yellow and red each of which is composed of the TrafficSystem and Reciever objects . TrafficSystem is the context class here and which has a reference to all the different states along with the current state. When a client calls change state we will be getting the current state from the TrafficSystem class and setting it to the next state in the flow. Reciever class is just an abstraction which actually talks to the GreenLight,RedLight and YellowLight device for switching it on and off. We also have an state called as previous state which we will be using to store the previous state of the system before changing the state in the Red and Green state so that we will know which state to set in the Yellow state based on the previous state as Yellow state is a common state of transition between the green and red.
public class Green implements State { private TrafficSystem trafficSystem; private Reciever reciever; public Green(TrafficSystem trafficSystem, Reciever reciever) { super(); this.trafficSystem = trafficSystem; this.reciever = reciever; } public TrafficSystem getTrafficSystem() { return trafficSystem; } public void setTrafficSystem(TrafficSystem trafficSystem) { this.trafficSystem = trafficSystem; } public Reciever getReciever() { return reciever; } public void setReciever(Reciever reciever) { this.reciever = reciever; } @Override public void changeState() { reciever.off(); trafficSystem.setPreviousState(this); trafficSystem.setCurrentState(trafficSystem.getYellowState()); } @Override public void displayState() { reciever.on(); } }
public class Red implements State { private TrafficSystem trafficSystem; private Reciever reciever; public Red(TrafficSystem trafficSystem, Reciever reciever) { super(); this.trafficSystem = trafficSystem; this.reciever = reciever; } public TrafficSystem getTrafficSystem() { return trafficSystem; } public void setTrafficSystem(TrafficSystem trafficSystem) { this.trafficSystem = trafficSystem; } public Reciever getReciever() { return reciever; } public void setReciever(Reciever reciever) { this.reciever = reciever; } @Override public void changeState() { reciever.off(); trafficSystem.setPreviousState(this); trafficSystem.setCurrentState(trafficSystem.getYellowState()); } @Override public void displayState() { reciever.on(); } }
public class Yellow implements State { private TrafficSystem trafficSystem; private Reciever reciever; public Yellow(TrafficSystem trafficSystem, Reciever reciever) { super(); this.trafficSystem = trafficSystem; this.reciever = reciever; } public TrafficSystem getTrafficSystem() { return trafficSystem; } public void setTrafficSystem(TrafficSystem trafficSystem) { this.trafficSystem = trafficSystem; } public Reciever getReciever() { return reciever; } public void setReciever(Reciever reciever) { this.reciever = reciever; } @Override public void changeState() { reciever.off(); if (trafficSystem.getPreviousState() instanceof Green) { trafficSystem.setCurrentState(trafficSystem.getRedState()); } else { trafficSystem.setCurrentState(trafficSystem.getGreenState()); } } @Override public void displayState() { reciever.on(); } }
Lets code the reciever classes
public interface Reciever { public void on(); public void off(); }
public class GreenLight implements Reciever { @Override public void on() { System.out.println("Swithcing on green light"); } @Override public void off() { System.out.println("Swithcing off green light"); } }
public class RedLight implements Reciever { @Override public void on() { System.out.println("Swithcing on red light"); } @Override public void off() { System.out.println("Swithcing off red light"); } }
public class YellowLight implements Reciever { @Override public void on() { System.out.println("Swithcing on yellow light"); } @Override public void off() { System.out.println("Swithcing off yellow light"); } }
Lets code the TrafficSystem class here we have a reference for all the states we have like red,green,yellow,current and previous. Once the client calls the changeState and displayState we will take the currentState object and delegate the work to be done to the state object.
public class TrafficSystem { private State greenState; private State redState; private State yellowState; private State currentState; private State previousState; public TrafficSystem() { super(); greenState = new Green(this, new GreenLight()); redState = new Red(this, new RedLight()); yellowState = new Yellow(this, new YellowLight()); Reciever intial = new GreenLight(); currentState = new Green(this, intial); previousState = new Green(this, intial); } public State getGreenState() { return greenState; } public void setGreenState(State greenState) { this.greenState = greenState; } public State getRedState() { return redState; } public void setRedState(State redState) { this.redState = redState; } public State getYellowState() { return yellowState; } public void setYellowState(State yellowState) { this.yellowState = yellowState; } public State getCurrentState() { return currentState; } public void setCurrentState(State currentState) { this.currentState = currentState; } public State getPreviousState() { return previousState; } public void setPreviousState(State previousState) { this.previousState = previousState; } public void changeState() { getCurrentState().changeState(); } public void displayState() { getCurrentState().displayState(); } }
Lets code the driver class. Here will have a yellow light displayed for 5 seconds which is basically a transition action and green and red for 30 seconds.
public class Test { public static void main(String[] args) throws InterruptedException { TrafficSystem trafficSystem = new TrafficSystem(); while (true) { trafficSystem.displayState(); if (trafficSystem.getCurrentState() instanceof Yellow) { Thread.sleep(5000); } else { Thread.sleep(15000); } trafficSystem.changeState(); } } }
Nice article
Nice
Nice,Thanks!
A great example of a state pattern. Thank you.