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.
trait State { def changeState() def 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.
class Green(trafficSystem: TrafficSystem, reciever: Reciever) extends State { def changeState() = { reciever.off() trafficSystem.previousState=this; trafficSystem.currentState=trafficSystem.yellow } def displayState() = { reciever.on() } }
class Red(trafficSystem: TrafficSystem, reciever: Reciever) extends State { def changeState() = { reciever.off() trafficSystem.previousState = this; trafficSystem.currentState = trafficSystem.yellow } def displayState() = { reciever.on } }
class Yellow(trafficSystem: TrafficSystem, reciever: Reciever) extends State { def changeState() = { reciever.off() if (trafficSystem.previousState.isInstanceOf[Green]) { trafficSystem.currentState = trafficSystem.red } else { trafficSystem.currentState = trafficSystem.green } } def displayState() = { reciever.on() } }
Lets code the reciever classes
trait Reciever { def on(); def off(); }
class GreenReciever extends Reciever { def on = { println("Swithing on green light") } def off = { println("Swithing off green light") } }
class RedReciever extends Reciever { def on = { println("Swithing on red light") } def off = { println("Swithing off red light") } }
class YellowReciever extends Reciever { def on = { println("Swithing on yellow light") } def off = { println("Swithing 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.
class TrafficSystem { var intialReciever:Reciever=new GreenReciever() var green:State=new Green(this,intialReciever) var red:State=new Red(this,new RedReciever) var yellow:State=new Yellow(this,new YellowReciever) var currentState:State=new Green(this,intialReciever) var previousState:State=new Green(this,intialReciever) def changeState() { currentState.changeState() } def displayState() { currentState.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.
object Test extends App{ val trafficSystem:TrafficSystem=new TrafficSystem() while(true) { trafficSystem.displayState() if(trafficSystem.currentState.isInstanceOf[Yellow]) { Thread.sleep(5000) trafficSystem.changeState() } else { Thread.sleep(30000) trafficSystem.changeState() } } }