Java > Design Patterns in Java > Behavioral Patterns > State Pattern

State Pattern Example: Document State Management

This example demonstrates the State pattern by modeling the different states of a document (Draft, Moderation, Published). The document's behavior changes based on its current state.

Introduction to the State Pattern

The State pattern is a behavioral design pattern that allows an object to alter its behavior when its internal state changes. This pattern encapsulates state-specific behavior into separate classes, known as state classes. The context object delegates behavior to its current state, allowing it to behave differently depending on the state.

DocumentState Interface

This interface defines the contract for all document states. It specifies the methods that can be called on a document in any state (in this example, `publish` and `moderate`).

interface DocumentState {
    void publish(Document document);
    void moderate(Document document);
}

DraftState Class

This class represents the 'Draft' state of the document. When `publish` is called, it transitions the document to the 'Moderation' state. `moderate` operation will output that the document is already in draft state.

class DraftState implements DocumentState {
    @Override
    public void publish(Document document) {
        System.out.println("Sending document to moderation...");
        document.setState(new ModerationState());
    }

    @Override
    public void moderate(Document document) {
        System.out.println("Document is already in draft state, nothing to moderate.");
    }
}

ModerationState Class

This class represents the 'Moderation' state. Calling `moderate` transitions the document to the 'Published' state. If already published it output to the console.

class ModerationState implements DocumentState {
    @Override
    public void publish(Document document) {
        System.out.println("Document is already in moderation.");
    }

    @Override
    public void moderate(Document document) {
        System.out.println("Moderating the document...");
        document.setState(new PublishedState());
    }
}

PublishedState Class

This class represents the 'Published' state. In this state, publishing or moderating is not allowed.

class PublishedState implements DocumentState {
    @Override
    public void publish(Document document) {
        System.out.println("Document is already published.");
    }

    @Override
    public void moderate(Document document) {
        System.out.println("Cannot moderate a published document.");
    }
}

Document Class (Context)

The `Document` class is the context. It holds the current `DocumentState` and delegates calls to the `publish` and `moderate` methods to the current state. The initial state is set to 'Draft'.

class Document {
    private DocumentState state;

    public Document() {
        this.state = new DraftState();
    }

    public void setState(DocumentState state) {
        this.state = state;
    }

    public void publish() {
        state.publish(this);
    }

    public void moderate() {
        state.moderate(this);
    }
}

Example Usage

This demonstrates how to use the State pattern. The `Document` object's behavior changes based on its current state.

public class StatePatternExample {
    public static void main(String[] args) {
        Document document = new Document();

        document.publish(); // Sends document to moderation
        document.moderate(); // Moderating the document...
        document.publish(); // Document is already published.
    }
}

Concepts Behind the Snippet

The core concept is to encapsulate state-specific behavior within state classes. This promotes the Single Responsibility Principle and Open/Closed Principle. The context object maintains a reference to the current state and delegates requests to it.

Real-Life Use Case

Consider a TCP connection. Its states can be: `CLOSED`, `LISTEN`, `SYN_SENT`, `SYN_RECEIVED`, `ESTABLISHED`, etc. The behavior of the connection changes dramatically depending on its current state. For example, what happens when data is received depends entirely on the connection's state.

Best Practices

  • Ensure each state class encapsulates a specific behavior.
  • Avoid using a large number of states, as it can complicate the system.
  • Consider using a state transition table to manage complex state transitions.

Interview Tip

Be prepared to explain the benefits of the State pattern compared to using conditional statements (if/else or switch) to manage state. Highlight the advantages of encapsulation, maintainability, and extensibility.

When to use them

Use the State pattern when:

  • An object's behavior depends on its state, and it must change its behavior at runtime depending on that state.
  • You have state-specific logic that should be encapsulated in separate classes.
  • You have a complex set of conditional statements based on the object's state.

Memory footprint

The memory footprint depends on the number of state objects and the data they hold. Generally, it's not a significant concern, but if you have a very large number of states or state objects, consider optimizing memory usage.

Alternatives

Alternatives to the State pattern include:

  • Using conditional statements (if/else or switch) to manage state. This is less maintainable for complex state transitions.
  • Using a finite state machine library.

Pros

  • Encapsulates state-specific behavior.
  • Promotes the Single Responsibility Principle.
  • Improves maintainability and extensibility.
  • Eliminates the need for large conditional statements.

Cons

  • Can increase the number of classes in the system.
  • Can be overkill for simple state management scenarios.

FAQ

  • What is the difference between the State and Strategy patterns?

    Both patterns encapsulate behavior in separate classes. However, the intent is different. The Strategy pattern is used to choose between different algorithms, while the State pattern is used to manage the state of an object over time.
  • When should I use the State pattern instead of simple if/else statements?

    Use the State pattern when your code involves a complex set of state-dependent behaviors and the state of the object changes frequently. If/else statements can become difficult to manage and maintain in such scenarios.