State design pattern is one of the behavioural design patterns.
This pattern is used when an Object changes its behaviour based on its internal state.
Example :
Assume some electronic device is an object which has 2 states called βONβ and βOFFβ
This object behaves differently when the state changes.
When the state is βONβ it will turn on the device and when the state is βOFFβ it will turn off the device
If we want to execute different behaviour based on state change, we can do it using if-else conditions
But, State pattern provides the solution for the same in a more loosely coupled way.
State pattern uses 2 important concepts called βcontextβ and βStateβ to achieve the same.
Letβs understand these 2 concepts
State defines a class with separate implementations for each behaviour with parent abstract class or Interface
Context is the class that has a reference to one of the concrete implementations of the State.
Context forwards the request to the state object for processing
Letβs try to understand State pattern with example
Consider above example of electronic device, it has 2 states βONβ and βOFFβ
Based on the state, corresponding behaviour of the device will change
If we implement this requirement without state pattern, we need to use if-else blocks as below
- package com.kb.state;
- public class ElectronicDevice {
- private String state;
- public void setState(String state){
- this.state=state;
- }
- public void execute(){
- if(state.equalsIgnoreCase("ON")){
- System.out.println("Device is turned ON");
- }else if(state.equalsIgnoreCase("OFF")){
- System.out.println("Device is turned OFF");
- }
- }
- public static void main(String args[]){
- ElectronicDevice device = new ElectronicDevice();
- device.setState("ON");
- device.execute();
- device.setState("OFF");
- device.execute();
- }
- }
package com.kb.state; public class ElectronicDevice { private String state; public void setState(String state){ this.state=state; } public void execute(){ if(state.equalsIgnoreCase("ON")){ System.out.println("Device is turned ON"); }else if(state.equalsIgnoreCase("OFF")){ System.out.println("Device is turned OFF"); } } public static void main(String args[]){ ElectronicDevice device = new ElectronicDevice(); device.setState("ON"); device.execute(); device.setState("OFF"); device.execute(); } }
If we observe this implementation, we could see that, if-else condition is tightly coupled with states.
Letβs try to implement the same using State design pattern
Step 1
First of all, We need to create State interface
This will define the method that should be implemented by different concrete states classes and context class.
- package com.kb.state;
- public interface State {
- public void execute();
- }
package com.kb.state; public interface State { public void execute(); }
We have defined State interface and declared a method called βexecute()β
Step 2
In our requirement, we will have 2 states β one for turning Device on and another to turn it off.
So, we need to create 2 concrete state implementations for these behaviours.
- package com.kb.state;
- public class DeviceOnState implements State {
- @Override
- public void execute() {
- System.out.println("Device is turned ON");
- }
- }
- package com.kb.state;
- public class DeviceOffState implements State {
- @Override
- public void execute() {
- System.out.println("Device is turned OFF");
- }
- }
package com.kb.state; public class DeviceOnState implements State { @Override public void execute() { System.out.println("Device is turned ON"); } } package com.kb.state; public class DeviceOffState implements State { @Override public void execute() { System.out.println("Device is turned OFF"); } }
Step 3
Create a context class which implements State and holds a reference to State object
- package com.kb.state;
- public class DeviceContext implements State {
- private State state;
- @Override
- public void execute() {
- state.execute();
- }
- public State getState() {
- return state;
- }
- public void setState(State state) {
- this.state = state;
- }
- }
package com.kb.state; public class DeviceContext implements State { private State state; @Override public void execute() { state.execute(); } public State getState() { return state; } public void setState(State state) { this.state = state; } }
We can see that Context class holds a reference to current state and makes a request to actual state implementation
Step 4
Letβs test this pattern by creating a client program
- package com.kb.state;
- public class Refrigerator {
- public static void main(String[] args) {
- DeviceContext context = new DeviceContext();
- State refrigeratorOnState = new DeviceOnState();
- State refrigeratorOffState = new DeviceOffState();
- context.setState(refrigeratorOnState);
- context.execute();
- context.setState(refrigeratorOffState);
- context.execute();
- }
- }
package com.kb.state; public class Refrigerator { public static void main(String[] args) { DeviceContext context = new DeviceContext(); State refrigeratorOnState = new DeviceOnState(); State refrigeratorOffState = new DeviceOffState(); context.setState(refrigeratorOnState); context.execute(); context.setState(refrigeratorOffState); context.execute(); } }
We can see that using State pattern we got the same output without writing any if-else blocks
Now its not tightly coupled with if-else blocks
If we want to add any new state, we can easily add it without worrying about if-else blocks
Consider an example that, we need to add new state for the device called βInstallingβ state
Now we just need to create this state concrete class and use it in client program
Letβs see how can we do it
Step 1
Create a new state class for Installing state
- package com.kb.state;
- public class DeviceInstallingState implements State {
- @Override
- public void execute() {
- System.out.println("Device is Installing , Please do not turnOff");
- }
- }
package com.kb.state; public class DeviceInstallingState implements State { @Override public void execute() { System.out.println("Device is Installing , Please do not turnOff"); } }
Step 2
Use the same in the client program as below
- State refrigeratorInstallState = new DeviceInstallingState();
- context.setState(refrigeratorInstallState);
- context.execute();
State refrigeratorInstallState = new DeviceInstallingState(); context.setState(refrigeratorInstallState); context.execute();
We are just creating an instance of new state and attaching it to context class.
Whole client class with previous states is as below
- package com.kb.state;
- public class Refrigerator {
- public static void main(String[] args) {
- DeviceContext context = new DeviceContext();
- State refrigeratorOnState = new DeviceOnState();
- State refrigeratorOffState = new DeviceOffState();
- context.setState(refrigeratorOnState);
- context.execute();
- context.setState(refrigeratorOffState);
- context.execute();
- State refrigeratorInstallState = new DeviceInstallingState();
- context.setState(refrigeratorInstallState);
- context.execute();
- }
- }
package com.kb.state; public class Refrigerator { public static void main(String[] args) { DeviceContext context = new DeviceContext(); State refrigeratorOnState = new DeviceOnState(); State refrigeratorOffState = new DeviceOffState(); context.setState(refrigeratorOnState); context.execute(); context.setState(refrigeratorOffState); context.execute(); State refrigeratorInstallState = new DeviceInstallingState(); context.setState(refrigeratorInstallState); context.execute(); } }
Advantages of State pattern
Whenever we need to add new state for additional behaviour, its very easy to achieve.
Helps to avoid if-else (or) switch conditions in the program to execute different behaviours
Code is more robust and easy to maintain
When to use this pattern?
We should use this pattern whenever the change in the state of an object changes its behaviour.
Hi KB,
I have one question related to Singleton Design pattern for multi threading.
public static LazySingletonDoubleLockCheck getInstance(){
if(singletonInstance==null){
synchronized (LazySingleton.class) {
if(singletonInstance ==null){
singletonInstance = new LazySingletonDoubleLockCheck();
}
}
}
return singletonInstance;
}
}
Cann’t we make the whole method as synchronized (public static synchronized LazySingletonDoubleLockCheck getInstance()) ?.
please correct me if my understanding is wrong
Hi KB,
Change the commented line number 26 at SerializeAndDeserializeTest.java in Approach 4 that is // Serialize to a file to Deserialize to a file just to avoid confusion especially for the beginners.
Consider it as a minor suggestion.
Can you plse update some more design pattern that we use in project .
thanks
raghavendra
Sure Raghavendra, its in my plan.
Thanks !!
thanks sir,
Looking forward which some more example on DP
Hello
I really liked your blog.however in this post i would like to add something ,
you have mentioned there that
*How we create object in Java ?
Yes using constructor , and we should not allow users to access constructor and execute it every time they try to. *
I believe this statement is completely wrong. constructor is executed that does not mean object is created. object is create only using new operator and constructor is executed to initialize the instance variable.
Kindly correct it.. π
thanks again.
Thanks PushP for bringing this into discussion!!
Let me try to make you understand the below line
we should not allow users to access constructor and execute it every time they try to.
If we are making someone to access constructor I mean execute constructor multiple times it just creates new object according to Java concept.
How ?
you know that constructors don’t have names right,so the only we to make the constructor execution is by creating an object using new.
I agree that constructor execution and object creation are different activities but my point here is when you allow someone to execute the constructor multiple times, it means you are calling constructors multiple times because there is no other way to execute the constructors.
Note:going through programmatically not through reflection and all.
your opinion
constructor is executed that does not mean object is created , object is created only using new operator and constructor is executed to initialize the instance variable
my understanding on your opinion
if I go by English i agree with this sentence , but going by Java concept i can not agree with this.
Because when constructor is executed it means that you have called it using new class_name(), it means one object gets created in Java.
I am not saying constructor execution and object creation are synonyms but what i am saying is they are mutually dependent.
So my point here is simple,
we should not allow users create object multiple times which is same as we should not allow users to execute the constructors multiple times.
preventing constructor execution is same as not allowing someone to create an object.
Happy to hear your inputs again and Thank you so much !!
Hi Mani,,,
I have added subscribe option on the right sidebar, you can subscribe there to receive the updates π
Thank you once again…and welcome for your comments always.
Mail me when you write a new one π
sure π
Hi Mani, Its really very good notice…
I forgot to add clone and also i didn’t add one more approach using ENUM.
But Reflection you are right , i didnt notice this situation. but its good to cover that as well.
so i will edit this post sometime to include all the above scenarios.
Thanks a lot for reading , observing the things and suggesting.
Keep Suggesting the posts during your free time π Thank you once again…
Good one, KB π
—
I can break your code using reflection though:
===
// Access constructor using reflection
Class clazz = FinalSingleton.class;
Constructor cons = clazz.getDeclaredConstructor();
cons.setAccessible(true);
FinalSingleton instanceThree = cons.newInstance();
instanceThree.setX(300);
System.out.println(“instanceThree.getX() :: “+instanceThree.getX());
===
the above creates a new instance.you can avoid that by changig your constructor
===
private FinalSingleton() {
//If-block needed to avoid creation of another object using reflection
/*if (LazyLoadFinalSingleton.SINGLETONINSTANCE != null) {
throw new IllegalStateException(“Inside FinalSingleton(): instance already created.”);
}*/
x = 100;
System.out.println(“Constructor Called. Instance created.”);
}
===
Also good to override clone method and throw clone not supported exception. You will never know where the loop hole is. π