State Design Pattern in Java

State Design Pattern in Java with Examples

In this article, I am going to discuss the State Design Pattern in Java with Examples. Please read our previous article where we discussed the Observer Design Pattern in Java with Examples. The State Design Pattern falls under the category of Behavioral Design Pattern. In this article, we will explore the fundamental principles, advantages, and potential disadvantages of the State design pattern, emphasizing its significance in simplifying state management, enabling code reuse, and promoting a modular and scalable design.

What is State Design Pattern?

According to Gang of Four Definitions, State Design Pattern allows an object to alter its behavior when its internal state changes. In simple words, we can say that the State Design Pattern allows an object to completely change its behavior depending on its current internal state.

In software development, managing the behavior of an object as it transitions through different states can be complex and error-prone. The State design pattern offers a solution by encapsulating each state into a separate object and allowing the object to change its behavior dynamically. By separating the state-specific logic from the object itself, the pattern promotes flexibility, extensibility, and maintainability.

For a better understanding, please have a look at the following diagram. Here, you can see on the left-hand side we have the client and on the right-hand side, we have the Context object and the context object internally maintains some states (such as State A and State B). As you can see in the below diagram, the current state of the Context object is State A. So, when the client makes a request to the Context object, what the Context object will do is, it will perform Operation A.

State Design Pattern in Java with Examples

Let us say the state of the context object is changed to State B. Now when the request is coming from the client, the context object will perform operation B as the current state is State B as shown in the following diagram.

State Design Pattern in Java with Examples

So, the point that you need to remember is that based on the current internal state of the context object, the behavior will be changed.

UML Diagram of State Design Pattern:

The State design pattern is a behavioral pattern that allows an object to alter its behavior when its internal state changes. It involves the following components:

UML Diagram of State Design Pattern

As you can see in the above image, the State Design Pattern consists of three components. They are as follows:

  • Context: Represents the object whose behavior changes based on its internal state. It maintains a reference to a State object and delegates state-specific operations to that object.
  • State: Defines the interface for all concrete state classes. It encapsulates the behavior associated with a particular state.
  • Concrete State: Implements the behavior associated with a specific state. Each concrete state class provides its own implementation of the state-specific operations defined by the State interface.
Real-Time Example to Understand State Design Pattern:

The Vending Machine is one of the best Real-Time Examples of the State Design Pattern. For example, you want to buy one product (Let’s say Pepsi) from the vending machine. Then what you have to do is, you have to select the product which is nothing but Pepsi and then you have to insert the money. Once you did that, then the Vending Machine will dispense the product. This is how the Vending Machine works in Real-Time.

Please have a look at the following diagram. Let us assume that the internal state of the Vending machine is Money Not Inserted and Product Not Selected. Then what are all the operations you can perform on the Vending Machine? You can select the product and insert the money but you cannot get the product from the vending machine.

Once you select the product and insert the money the state of the Vending machine changed from Money Not Inserted and Product Not Selected to Money Inserted and Product Selected State. Once the state change to Money Inserted and Product Selected, then what the Vending will do is, it will give you the product as well as the balance amount if any.

Real-Time Example to Understand State Design Pattern

This is how the Vending Machine works and based on the internal state the behavior of the vending machine will change.

Implementing State Design Pattern in Java:

The State Design Pattern proves to be a valuable asset in systems that exhibit different behaviors based on their internal state. In a company, there are usually several departments that require the same resource. For example, the finance, sales, and management departments all require access to the financial database. However, when one department has the resource, other departments should not be able to access it. The UML Diagram of this example is given below using State Design Pattern.

Implementing State Design Pattern in Java

Step 1: Create a new directory to store all the class files of this project.

Step 2: Open VS Code and create a new project, called state.

Step 3: In the project, create a new file called Resource.java. Add the following code to the file:

public interface Resource
{
    public void open();
    public void close();
    public void log();
}

This is the interface from which other concrete classes will extend.

Step 4: In the project, create three new files called Accounting.java, Sales.java, and Management.java. All three of these files extend from the Resource interface. Add the following code to the files:

Accounting.java
public class Accounting implements Resource
{
    @Override
    public void open()
    {System.out.println("Resource reserved by accounting dept.");}

    @Override
    public void close()
    {System.out.println("Resource released by accounting dept.");}

    @Override
    public void log()
    {System.out.println("Resource is being handled by accounting dept.");}
}
Sales.java
public class Sales implements Resource
{
    @Override
    public void open()
    {System.out.println("Resource reserved by sales dept.");}

    @Override
    public void close()
    {System.out.println("Resource released by sales dept.");}

    @Override
    public void log()
    {System.out.println("Resource is being handled by sales dept.");}
}
Management.java
public class Management implements Resource
{
    @Override
    public void open()
    {System.out.println("Resource reserved by management");}

    @Override
    public void close()
    {System.out.println("Resource released by management");}

    @Override
    public void log()
    {System.out.println("Resource is being handled by management");}
}

Step 5: In the project, create a new file called Controller.java. Add the following code to the file:

public class Controller
{
    public static Accounting a;
    public static Sales s;
    public static Management m;
    private static Resource r;

    public Controller()
    {
        a = new Accounting();
        s = new Sales();
        m = new Management();
    }

    public void giveResourceToAccounting()  {r = a;}
    public void giveResourceToSales()       {r = s;}
    public void giveResourceToManagement()  {r = m;}

    public void open()   {r.open();}
    public void close()  {r.close();}
    public void log()    {r.log();}
}

Step 6: In the project, create a new file called StatePatternDemo.java. This class will contain the main() function. Add the following code to StatePatternDemo.java:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class StatePatternDemo
{
    Controller c;
    
    public StatePatternDemo(String r)
    {
        c = new Controller();

        if (r.equalsIgnoreCase("accounting"))
            c.giveResourceToAccounting();
        else if (r.equalsIgnoreCase("sales"))
            c.giveResourceToSales();
        else if (r.equalsIgnoreCase("management"))
            c.giveResourceToManagement();
        
        c.open();
        c.log();
        c.close();
    }

    public static void main(String[] args) throws IOException
    {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.print("Enter dept. name: ");
        new StatePatternDemo(br.readLine());
    }
}

This main() function creates a subject. It then creates three observers, one of each concrete class created in step 5. It then changes the state of the subject. The observers have to reflect on this properly.

Step 7: Compile and execute the application. Ensure compilation is successful. Verify that the program works as expected.

State Design Pattern in Java with Examples

Congratulations! You now know how to implement observer patterns!

UML Diagram of State Design Pattern:

Now, let us see the State Design Pattern UML Diagram Components with our Example so that you can easily understand the UML Diagram.

UML Diagram of State Design Pattern

The classes can be described as follows:

  1. ResourceHandler: This is the interface that will define the basic methods that will be implemented by the concrete resource handler class.
  2. ConcreteResourceHandler: This class implements the ResourceHandler interface. It implements the functions defined in the interface.
  3. Handler: This object is another object that will be used by the main() function. The responsibility of this class is to handle the objects of type ResourceHandler.
  4. DriverClass: This class contains the main() function and is responsible for the simulation of the program.
Advantages of State Design Pattern in Java

The advantages of using the State Design Pattern in Java are as follows:

  • Simplified State Management: The State pattern simplifies the management of object behavior in different states. By encapsulating each state into a separate object, the pattern eliminates complex conditional statements and promotes a cleaner and more modular code structure. Changes in behavior can be implemented by adding new state objects or modifying existing ones, making it easier to understand and maintain the codebase.
  • Code Reuse: The State pattern promotes code reuse by allowing the sharing of state-specific behavior among multiple objects. Multiple objects can refer to the same state object, eliminating the need to duplicate code for each object. This leads to more efficient and maintainable code, as changes to the state-specific behavior only need to be made in one place.
  • Flexible and Extensible Design: The State pattern facilitates flexibility and extensibility in the design. New states can be added easily by creating new concrete state classes that adhere to the State interface. This allows for the seamless integration of new behaviors or variations in the object’s behavior without modifying the existing code.
  • Improved Maintainability: Separating state-specific behavior into individual state objects improves the maintainability of the codebase. Each state is encapsulated within its own class, making it easier to understand, test, and modify. Modifications to the behavior of one state do not impact other states or the context object, ensuring better code modularity and reducing the risk of unintended side effects.
  • Enhanced Testability: The State pattern enhances the testability of the system. Each state can be tested in isolation, allowing for targeted testing of specific behaviors. This enables more comprehensive testing and facilitates the identification and resolution of potential issues.
Disadvantages of State Design Pattern in Java

The disadvantages of using the State Design Pattern in Java are as follows:

  • Increased Complexity: Implementing the State pattern introduces additional complexity to the codebase. The separation of behavior into multiple state objects may require a more intricate system structure and increase the number of classes involved. This complexity should be carefully managed to avoid unnecessary overhead and potential confusion.
  • Increased Number of Classes: The State pattern may result in an increased number of classes, especially in systems with a large number of states. Each state typically requires its own concrete state class, which can lead to a larger codebase and potentially impact the overall system performance.
  • Object Size: The State pattern may increase the memory footprint of the objects, especially if there are many state-specific objects created and maintained by the context object. This can be a concern in resource-constrained environments or when dealing with large-scale systems.
  • Potential Overuse: The State pattern should be used judiciously and only when there is a clear need for dynamic behavior changes. Overusing the pattern can lead to unnecessary complexity and reduced code readability. It is essential to carefully analyze the requirements and determine if the State pattern is the most suitable solution.

In the next article, I am going to discuss Strategy Design Pattern in Java with Examples. Here, in this article, I try to explain State Design Pattern in Java with Examples. I hope you understood the need for and use of the State Design Pattern in Java.

Leave a Reply

Your email address will not be published. Required fields are marked *