The State pattern comes under the Behavioral Design Pattern family of GOF pattern, it changes the state of the object as varies the behavior of context object.
The State Pattern
According to the Gang of Four:
Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
The state behavioral pattern is one of the behavioral software design patterns. The state pattern defines and implements a state machine. With the state design, a state machine is executed by actualizing every individual state as an inferred class of the state design interface and actualizing state advances by summoning techniques characterized by the pattern’s superclass.
The state pattern can be analyzed as a strategy pattern. The strategy pattern is able to change or alter the defined strategy by acknowledging the techniques that are defined in the interface of the pattern. The state behavioral pattern is used in computer programming. The pattern is needed to enclose the changing behavior of the same object depending on its internal state. This way, it is easier and cleaner to store an object’s changing behavior at run-time, without having to restore to large conditional statements. This way, the maintainability of the system is improved.
Spring 5 Design Pattern Book
Benefits of the State Design Pattern:
There are following lists the benefits of using the State pattern:
- Localizes state-specific behavior and partitions behavior for different states .
- Make state transitions explicit.
also read:
Understanding State Design Pattern
To design, versatile and reusable object-oriented software, there is 23 well-known Gang of Four (GoF) design patterns, the state pattern is one of them. There are certain problems that the state behavioral pattern cater. Any object, in the system, is supposed to change its behavior on account of the change of its internal state. The object which shows state-specific behavior must be defined independently, which means that the new states must be added and the behavior of the current states must be changed independently.
To implement a state-specific behavior directly in a class creates inflexibility because it tightly couples the state machine to a certain behavior. With the state machine and behaviors tightly coupled, it is difficult, almost impossible to add new states or change the behavior of a current state independently, without having to change the class. The separate state objects are defined in the state behavioral pattern, which encases the behavior for each state that is state-specific. For performing a state-specific behavior and defining classes which are capable of implementing the interface for each state, and interface (state) is defined.
The state pattern defines a class which assigns state-specific behavior to its existing state object. It does not implement the state-specific behavior directly. This does not let a class depend on how state-specific behavior is implemented. By defining new state classes, it is possible to add new classes. A class is able to alter its behavior at run-time, by altering its existing state object. The structure of the state behavioral pattern is such that there are Context, State, and Concrete state subclasses. The context characterizes the interface important to customers and keeps up an occasion of a ConcreteState subclass that characterizes the present state.
The state defines an interface for encapsulating the behavior related together with a certain state of the Context. Every subclass of concrete state subclasses is able to implement a behavior related to the state of the context. The state-specific requests are assigned to the present concrete state object by the context. A context is capable of passing itself to the state object, which handles the request, as an argument. This allows the state object, access to the context.
UML Class Diagram for State Pattern
Let’s see the following UML diagram is showing the all components of State design pattern:
Context
It is an interface for client to perform some task and maintains an instance of a ConcreteState subclass that defines the current state.
State
It is an interface that encapsulates the behavior associated with a particular state of the Context.
ConcreteState
It is a concrete class that implements a behavior associated with a state of the Context.
Example of State Design Pattern Implementation
This real-world code demonstrates the State pattern which allows an Account to behave differently depending on its balance. The difference in behavior is delegated to State objects called RedState, SilverState and GoldState. These states represent overdrawn accounts, starter accounts, and accounts in good standing.
Step 1: Create an interface.
State.java
/** * */ package com.doj.patterns.behavior.state; /** * @author Dinesh.Rajput * */ public interface State { void deposit(double amount); void withdraw(double amount); void payInterest(); Account getAccount(); double getBalance(); }
Step 2: Create concrete classes implementing the same interface.
RedState.java
/** * */ package com.doj.patterns.behavior.state; /** * @author Dinesh.Rajput * */ public class RedState implements State { private Account account; private double balance; private double upperLimit; private double serviceFee; public RedState(State state){ this(state.getAccount(), state.getBalance()); } public RedState(Account account, double balance) { super(); this.account = account; this.balance = balance; this.upperLimit = 0.0; this.serviceFee = 15.00; } @Override public void deposit(double amount) { balance += amount; stateChangeCheck(); } @Override public void withdraw(double amount) { balance = balance - serviceFee; System.out.println("No funds available for withdrawal!"); } @Override public void payInterest() { //No interest in this state of account } public Account getAccount() { return account; } public void setAccount(Account account) { this.account = account; } public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } private void stateChangeCheck() { if (balance > upperLimit){ account.setState(new SilverState(this)); } } }
SilverState.java
package com.doj.patterns.behavior.state; /** * @author Dinesh.Rajput * */ public class SilverState implements State { private Account account; private double balance; private double interest; private double lowerLimit; private double upperLimit; public SilverState(State state){ this(state.getAccount(), state.getBalance()); } public SilverState(Account account, double balance) { super(); this.account = account; this.balance = balance; this.interest = 0.0; this.lowerLimit = 0.0; this.upperLimit = 1000.0; } @Override public void deposit(double amount) { balance += amount; stateChangeCheck(); } @Override public void withdraw(double amount) { balance -= amount; stateChangeCheck(); } private void stateChangeCheck() { if (balance < lowerLimit){ account.setState(new RedState(this)); }else if (balance > upperLimit){ account.setState(new GoldState(this)); } } @Override public void payInterest() { balance += interest * balance; stateChangeCheck(); } @Override public Account getAccount() { return this.account; } @Override public double getBalance() { return this.balance; } }
GoldState.java
package com.doj.patterns.behavior.state; /** * @author Dinesh.Rajput * */ public class GoldState implements State { private Account account; private double balance; private double interest; private double lowerLimit; public GoldState(State state){ this(state.getAccount(), state.getBalance()); } public GoldState(Account account, double balance) { super(); this.account = account; this.balance = balance; this.interest = 0.05; this.lowerLimit = 1000.0; } @Override public void deposit(double amount) { balance += amount; stateChangeCheck(); } @Override public void withdraw(double amount) { balance -= amount; stateChangeCheck(); } private void stateChangeCheck() { if (balance < 0.0){ account.setState(new RedState(this)); }else if (balance < lowerLimit){ account.setState(new SilverState(this)); } } @Override public void payInterest() { balance += interest * balance; stateChangeCheck(); } @Override public Account getAccount() { return this.account; } @Override public double getBalance() { return this.balance; } }
Step 3: Create Context Class.
Account.java
/** * */ package com.doj.patterns.behavior.state; /** * @author Dinesh.Rajput * */ public class Account { private State state; private String owner; // New accounts are 'Silver' by default public Account(String owner) { super(); this.state = new SilverState(this, 0.0); this.owner = owner; } public void deposit(double amount) { state.deposit(amount); System.out.println("Deposited --- "+amount); System.out.println("Balance --- "+this.state.getBalance()); System.out.println("Status --- "+this.state.getClass().getSimpleName()); System.out.println("--------------------------------"); } public void withdraw(double amount){ state.withdraw(amount); System.out.println("Withdrew --- "+amount); System.out.println("Balance --- "+this.state.getBalance()); System.out.println("Status --- "+this.state.getClass().getSimpleName()); System.out.println("--------------------------------"); } public void payInterest(){ state.payInterest(); System.out.println("Interest Paid --- "); System.out.println("Balance --- "+this.state.getBalance()); System.out.println("Status --- "+this.state.getClass().getSimpleName()); System.out.println("--------------------------------"); } public void setState(State state) { this.state = state; } public void setOwner(String owner) { this.owner = owner; } public State getState() { return state; } public String getOwner() { return owner; } }
Step 4: Use the Context to see change in behavior when State changes.
StatePatternDemo.java
/** * */ package com.doj.patterns.behavior.state; /** * @author Dinesh.Rajput * */ public class StatePatternDemo { /** * @param args */ public static void main(String[] args) { // Open a new account Account account = new Account("Dinesh Rajput"); // Apply financial transactions account.deposit(500.0); account.deposit(300.0); account.deposit(550.0); account.payInterest(); account.withdraw(2000.00); account.withdraw(1100.00); } }
Step 5: Let’s run this demo class and verify the output.
Deposited --- 500.0 Balance --- 500.0 Status --- SilverState -------------------------------- Deposited --- 300.0 Balance --- 800.0 Status --- SilverState -------------------------------- Deposited --- 550.0 Balance --- 1350.0 Status --- GoldState -------------------------------- Interest Paid --- Balance --- 1417.5 Status --- GoldState -------------------------------- Withdrew --- 2000.0 Balance --- -582.5 Status --- RedState -------------------------------- No funds available for withdrawal! Withdrew --- 1100.0 Balance --- -597.5 Status --- RedState --------------------------------