Skip to main content

Introduction To The State Design Pattern

State Design Pattern is a behavioral design pattern that allows an object to change its behavior when its internal state changes.

The pattern extracts state-related behaviors into separate state classes and forces the original object to delegate the work to an instance of these classes, instead of acting on its own. The pattern introduces a state interface and extracts all state-specific methods into a set of concrete state classes. The original object, called context, stores a reference to one of the state objects that represents its current state, and delegates all the state-related work to this object.

Classic Imeplementation Of The State Design Pattern

Here's a basic example using TypeScript to create a simple light switch with " On" and "Off" states:

interface LightState {
switchState(lightSwitch: LightSwitch): void;
}

class OnState implements LightState {
switchState(lightSwitch: LightSwitch): void {
console.log("Light is already On. Turning Off...");
lightSwitch.setState(new OffState());
}
}

class OffState implements LightState {
switchState(lightSwitch: LightSwitch): void {
console.log("Light is Off. Turning On...");
lightSwitch.setState(new OnState());
}
}

class LightSwitch {
private state: LightState;

constructor(state: LightState) {
this.state = state;
}

setState(state: LightState) {
this.state = state;
}

press() {
this.state.switchState(this);
}
}

// Usage
const lightSwitch = new LightSwitch(new OffState());
lightSwitch.press(); // Light is Off. Turning On...
lightSwitch.press(); // Light is already On. Turning Off...

In the example above, LightSwitch is the context, and LightState is the state interface. OnState and OffState are the concrete state classes. We initialize the LightSwitch with an initial state (OffState). When we call lightSwitch.press(), it delegates the request to the current state object to handle it. The state object processes the request and then switches to the next state by calling lightSwitch.setState(new NextState()).

The State pattern is used when an object's behavior should change along with its state, and when complex conditions tie object behavior to its state. It's a good way to organize related behaviors that are exclusive to specific states, and when a class has a massive conditional operator that switches between different states.

When To Use State Pattern

State pattern can be considered a potential solution to the following situations or "smells" in your code:

  1. Large conditionals or switch-case statements based on object state: If you have an object that behaves differently based on its internal state, and you're using a lot of if-else or switch-case statements to handle this, it can be a signal that the state pattern could be useful.

  2. State transitions are complex or error-prone: When the logic to transition between states becomes complex and hard to manage, using the state pattern can help. Each state class will be responsible for deciding what the next state should be, reducing the risk of errors.

  3. State-specific behavior is spread out throughout your code: If you find that methods contain state-specific behavior and this behavior is spread out throughout your code, it may be time to consider the state pattern. The state pattern encapsulates the state-specific behavior in individual classes. This makes it easier to modify behavior related to a specific state, as it's all located in one place.

  4. High coupling between states and behaviors: If you find a strong coupling between the states of an object and its behaviors, the state pattern can be considered. With the state pattern, the coupling becomes more manageable since state and related behavior are encapsulated in separate classes.

  5. Code is hard to extend with new states: If adding a new state to your object requires modifying existing code and adding new if-else or switch-case clauses, the State pattern can be beneficial. When using the State pattern, adding a new state can be done by creating a new state class, which leaves the existing code untouched and adheres to the Open/Closed Principle.

Remember, design patterns aren't a silver bullet and they come with their own complexity. They should be used judiciously, and the State pattern is no exception. It adds a layer of abstraction and can make your code more complex, so it's best to use when the complexity it helps manage is greater than the complexity it introduces.

TypeScript Course Instructor Image
TypeScript Course Instructor Image

Time To Transition From JavaScript To TypeScript

Level Up Your TypeScript And Object Oriented Programming Skills. The only complete TypeScript course on the marketplace you building TypeScript apps like a PRO.

SEE COURSE DETAILS

What Can You Do Next 🙏😊

If you liked the article, consider subscribing to Cloudaffle, my YouTube Channel, where I keep posting in-depth tutorials and all edutainment stuff for software developers.

YouTube @cloudaffle