Discover how a simple design trick can turn your tangled state logic into clean, manageable code!
Why State pattern in LLD? - Purpose & Use Cases
Start learning this pattern below
Jump into concepts and practice - no test required
Imagine you are building a vending machine that can be in different states like 'Idle', 'HasMoney', 'Dispensing', or 'OutOfStock'. You try to handle all these states using many if-else or switch-case statements scattered everywhere in your code.
This manual approach quickly becomes messy and hard to maintain. Adding a new state means changing many places, increasing bugs and confusion. It's like juggling too many balls at once and dropping them often.
The State pattern organizes each state into its own class with clear behavior. The vending machine just delegates actions to the current state object. This keeps code clean, easy to extend, and reduces errors.
if(state == 'Idle') { if(moneyInserted) state = 'HasMoney'; } else if(state == 'HasMoney') { if(selectItem) state = 'Dispensing'; }
state.handleMoneyInserted(); // state object changes itself internally
It enables building flexible systems where adding or changing states is simple and safe without breaking existing code.
Think of a music player app that changes behavior when playing, paused, or stopped. Using the State pattern, each mode handles play, pause, or stop commands differently without complex condition checks.
Manual state handling leads to complex, error-prone code.
State pattern encapsulates state-specific behavior in separate classes.
It makes systems easier to extend and maintain.
Practice
State pattern in system design?Solution
Step 1: Understand the role of the State pattern
The State pattern helps an object change its behavior based on its internal state without changing its class.Step 2: Compare with other design patterns
Other options describe Singleton (A), Prototype (B), and Builder (C) patterns, which are unrelated to state behavior changes.Final Answer:
To allow an object to change its behavior when its internal state changes -> Option CQuick Check:
State pattern = behavior change by internal state [OK]
- Confusing State pattern with Singleton or Builder patterns
- Thinking it manages object creation instead of behavior
- Assuming it provides global access to resources
Solution
Step 1: Identify the correct interface syntax
The State pattern requires a State interface with a method likehandle()to define behavior.Step 2: Eliminate incorrect options
class State { void handle() {} } is a class, not an interface; C is an enum, not behavior; D is a struct without behavior.Final Answer:
interface State { void handle(); } -> Option AQuick Check:
State interface defines behavior method [OK]
- Using enum or struct instead of interface/class for behavior
- Defining empty methods without interface
- Confusing class and interface roles
class Context {
State state;
void request() { state.handle(this); }
void setState(State s) { state = s; }
}
class State {
void handle(Context c) { c.setState(new StateB()); }
}
class StateB extends State {
void handle(Context c) { c.setState(new State()); }
}
Context ctx = new Context();
ctx.setState(new State());
ctx.request();
ctx.request();
What is the final state of ctx after these two requests?Solution
Step 1: Trace first request()
Initially, ctx.state = State instance. Calling request() calls State.handle(ctx), which sets state to new StateB.Step 2: Trace second request()
Now ctx.state = StateB instance. Calling request() calls StateB.handle(ctx), which sets state back to new State.Final Answer:
An instance of State -> Option AQuick Check:
State and StateB toggle on requests [OK]
- Assuming state stays the same after first request
- Confusing which handle method is called
- Ignoring state changes inside handle methods
interface State {
void handle(Context c);
}
class Context {
State state;
void request() {
state.handle(this);
}
}
class ConcreteStateA implements State {
void handle(Context c) {
// Missing state transition
System.out.println("State A handling");
}
}
Context ctx = new Context();
ctx.state = new ConcreteStateA();
ctx.request();
ctx.request();
Solution
Step 1: Analyze state transitions in handle()
ConcreteStateA.handle() prints a message but does not update Context's state, so no state change occurs.Step 2: Check other options
State interface method is correct; Context initializes state before request; ConcreteStateA implements handle properly.Final Answer:
Context's state is never updated inside handle, so state never changes -> Option BQuick Check:
State transition missing inside handle() [OK]
- Forgetting to update state inside handle method
- Assuming printing is enough for state change
- Ignoring initialization of state before request
Solution
Step 1: Understand State pattern application
The pattern suggests encapsulating each state in its own class with behavior and transitions.Step 2: Evaluate options for scalability and clarity
Create separate state classes for Green, Yellow, and Red, each implementing anext()method to switch to the next state cleanly separates states and their transitions. Options B, C, and D mix logic or lack encapsulation, reducing maintainability.Final Answer:
Create separate state classes for Green, Yellow, and Red, each implementing a next() method to switch to the next state -> Option DQuick Check:
Separate classes with transitions = State pattern [OK]
- Using if-else instead of separate state classes
- Not encapsulating state behavior inside classes
- Relying on external variables without behavior
