State Design Pattern

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()
}

 

follow us on