State Design Pattern
also known as Objects for States
, comes under Behavioural Design Pattern
, An object
at any given moment of time during its life cycle
can be within a finite number of state
. The object can be transitioned
from current state
to previous state
or next state
as per requirement. to maintain multiple statues
either switch
or if-else
is required which makes it complex and hard to manage, a better way is to use State Design Pattern
, it provides a loosely couped
method to maintain different states
. The same function exhibits polymorphism
ie behaves differently depending upon the current state.
The state object
should be Singleton
so that only 1 instance should be maintained throughout the application
.
State Design Pattern allows changing its behavior from one state to another when its current state changes, it is very close to finite-state machines.
Each state controlls
and encapsulates
the logic within, hence following the Single Responsibility Principal
.
Adding a new State
doesn't require existing code modification ie close
for modification
, but a new State
can always be added hence following the Open/closed principle
.
Some real-world examples of State Design Patterns are
A remote controls
the electronic devices. it has ON
state and OFF
state, the next state will be determined by the current state only ie if the device is OFF
then the next state is ON
else OFF
.
Thread
in Java supports multiple states such as New
, Runnable or Running
,Blocked
, Waiting
,Timed Waiting
, Terminated
, also known as life cycle state. the next state
is determined by the current state
.
A E-commerce
Item can have multiple states
such as Order Acknowldege
, Shipped
, transition
, out for delivery
and delivered
ect, the next state
is determined by the current state
.
A document
have 3 states, new
, draft
and published
, the next state
is determined by its current state
.
A smart phone
buttons perform different functions depending on the current state
.
package org.wesome.design.patterns;
public interface AppleState {
String nextState(Apple apple);
String prevState(Apple apple);
String currentState();
}
package org.wesome.design.patterns;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
public class Apple {
private AppleState appleState = new SeedState();
public String setAppleState(AppleState appleState) {
this.appleState = appleState;
return appleState.currentState();
}
public String previousState() {
return appleState.prevState(this);
}
public String nextState() {
return appleState.nextState(this);
}
public String getCurrentStatus() {
return appleState.currentState();
}
}
package org.wesome.design.patterns;
public class SeedState implements AppleState {
public static final String CURRENT_STATE = "Apple is at seed state and ready for planting";
public static final String PREV_STATE = "Apple is at root state, Preparing for Apple farm";
@Override
public String nextState(Apple apple) {
return apple.setAppleState(new PlantState());
}
@Override
public String prevState(Apple apple) {
apple.setAppleState(null);
return PREV_STATE;
}
@Override
public String currentState() {
return CURRENT_STATE;
}
}
package org.wesome.design.patterns;
public class PlantState implements AppleState {
public static final String CURRENT_STATE = "Apple is at plant state and requires to be watered";
@Override
public String nextState(Apple apple) {
return apple.setAppleState(new TreeState());
}
@Override
public String prevState(Apple apple) {
return apple.setAppleState(new SeedState());
}
@Override
public String currentState() {
return CURRENT_STATE;
}
}
package org.wesome.design.patterns;
public class TreeState implements AppleState {
public static final String CURRENT_STATE = "Apple is at tree state and ready for harvesting";
@Override
public String nextState(Apple apple) {
return apple.setAppleState(new FruitState());
}
@Override
public String prevState(Apple apple) {
return apple.setAppleState(new PlantState());
}
@Override
public String currentState() {
return CURRENT_STATE;
}
}
package org.wesome.design.patterns;
public class FruitState implements AppleState {
public static final String CURRENT_STATE = "Apple is at fruit state and ready for the market";
public static final String NEXT_STATE = "I ate Apple, and it was good";
@Override
public String nextState(Apple apple) {
return NEXT_STATE;
}
@Override
public String prevState(Apple apple) {
return apple.setAppleState(new TreeState());
}
@Override
public String currentState() {
return CURRENT_STATE;
}
}
package org.wesome.design.patterns;
public class State {
public static void main(String[] args) {
Apple apple = new Apple();
apple.setAppleState(new SeedState());
System.out.println(apple.getCurrentStatus());
apple.nextState();
System.out.println(apple.getCurrentStatus());
apple.nextState();
System.out.println(apple.getCurrentStatus());
apple.nextState();
System.out.println(apple.getCurrentStatus());
System.out.println(apple.nextState());
}
}
package org.wesome.design.patterns;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class StateTest {
@Test
void testSeedState() {
Apple apple = new Apple();
apple.setAppleState(new SeedState());
Assertions.assertEquals(SeedState.CURRENT_STATE, apple.getCurrentStatus());
Assertions.assertEquals(PlantState.CURRENT_STATE, apple.nextState());
Assertions.assertEquals(SeedState.CURRENT_STATE, apple.previousState());
}
@Test
void testPlantState() {
Apple apple = new Apple();
apple.setAppleState(new SeedState());
apple.nextState();
Assertions.assertEquals(PlantState.CURRENT_STATE, apple.getCurrentStatus());
Assertions.assertEquals(TreeState.CURRENT_STATE, apple.nextState());
Assertions.assertEquals(PlantState.CURRENT_STATE, apple.previousState());
}
@Test
void testTreeState() {
Apple apple = new Apple();
apple.setAppleState(new SeedState());
apple.nextState();
apple.nextState();
Assertions.assertEquals(TreeState.CURRENT_STATE, apple.getCurrentStatus());
Assertions.assertEquals(FruitState.CURRENT_STATE, apple.nextState());
Assertions.assertEquals(TreeState.CURRENT_STATE, apple.previousState());
}
@Test
void testFruitState() {
Apple apple = new Apple();
apple.setAppleState(new SeedState());
System.out.println(apple.getCurrentStatus());
apple.nextState();
apple.nextState();
apple.nextState();
Assertions.assertEquals(FruitState.CURRENT_STATE, apple.getCurrentStatus());
Assertions.assertEquals(FruitState.NEXT_STATE, apple.nextState());
Assertions.assertEquals(TreeState.CURRENT_STATE, apple.previousState());
}
}
plugins {
id 'java'
id "io.freefair.lombok" version "6.2.0"
}
group = 'org.wesome'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.junit.jupiter:junit-jupiter:5.6.2'
}
test {
useJUnitPlatform()
}