State Design Pattern in JavaScript

1. Definition

The State Design Pattern allows an object to change its behavior when its internal state changes. The object will appear to change its class.

2. Problem Statement

Imagine an object, like a traffic light, that can have different states (e.g., red, yellow, green), and its behavior changes based on its current state. Implementing such state-specific behaviors directly within the object can make the code complex, hard to maintain, and error-prone.

3. Solution

Encapsulate state-specific behaviors into separate state classes. The main object will have a reference to the current state object, which represents its current state, and delegates state-specific behavior to it.

4. Real-World Use Cases

1. A traffic light system with different colors representing different behaviors.

2. Media player that has different states like play, pause, stop, etc.

3. Game characters that can be in states like idle, running, attacking, etc.

5. Implementation Steps

1. Define a State interface with methods representing state-specific behaviors.

2. Create concrete state classes implementing the State interface.

3. The main object (Context) has a reference to the current state and delegates state-specific behaviors to this current state object.

6. Implementation in JavaScript

// State Interface
class State {
    handle(context) {
        // to be overridden by concrete states
    }
}
// Concrete States
class RedState extends State {
    handle(context) {
        console.log("Red Light");
        context.setState(new GreenState());
    }
}
class GreenState extends State {
    handle(context) {
        console.log("Green Light");
        context.setState(new YellowState());
    }
}
class YellowState extends State {
    handle(context) {
        console.log("Yellow Light");
        context.setState(new RedState());
    }
}
// Context Class
class TrafficLight {
    constructor() {
        this._state = new RedState();  // Initial state
    }
    setState(state) {
        this._state = state;
    }
    change() {
        this._state.handle(this);
    }
}
// Client Code
const trafficLight = new TrafficLight();
trafficLight.change();  // Red Light
trafficLight.change();  // Green Light
trafficLight.change();  // Yellow Light

Output:

Red Light
Green Light
Yellow Light

Explanation:

1. A State interface is defined with a handle method which encapsulates state-specific behavior.

2. Concrete states (RedState, GreenState, and YellowState) are implemented which override the handle method. Each state transitions to another state after handling.

3. The TrafficLight (context) class maintains a reference to its current state. When the change method is called, it delegates the behavior to its current state.

4. In the client code, calling the change method on TrafficLight demonstrates how the state changes and the corresponding behavior is executed.

7. When to use?

The State Pattern is useful when:

1. An object's behavior changes based on its state, and it can have numerous states.

2. State-specific logic is spread across multiple conditional statements. Using the State pattern can help in organizing the code.

3. The application needs to be extensible, allowing for easy addition of new states and behaviors.


Comments