| Users / Instances | State Objects | Memory Usage | Performance Impact | Complexity |
|---|---|---|---|---|
| 100 | Few (shared or per instance) | Low | Negligible | Simple to manage |
| 10,000 | Many (per instance or shared) | Moderate | Manageable | Need clear state management |
| 1,000,000 | Very many (likely shared states) | High | Potential overhead in state transitions | Requires optimization and pooling |
| 100,000,000 | Extremely many (must share states) | Very high | State management can become bottleneck | Need distributed state handling |
State pattern in LLD - Scalability & System Analysis
Start learning this pattern below
Jump into concepts and practice - no test required
The first bottleneck is memory usage and CPU overhead due to many state objects and frequent state transitions. As the number of instances grows, creating separate state objects per instance increases memory consumption. Also, complex state transition logic can slow down processing.
- State Sharing: Use shared immutable state objects to reduce memory usage.
- State Pooling: Reuse state objects instead of creating new ones for each instance.
- Lazy Initialization: Initialize states only when needed to save resources.
- Distributed State Management: For very large scale, distribute state handling across multiple servers or services.
- Optimize State Transitions: Simplify transition logic to reduce CPU overhead.
Assuming each state object uses ~1KB memory:
- At 10,000 instances with 5 states each: 10,000 * 5 * 1KB = ~50MB memory.
- At 1,000,000 instances: 1,000,000 * 5 * 1KB = ~5GB memory (too large for single server).
- CPU: Frequent state transitions at high scale can cause CPU spikes; optimize logic.
- Network: If states are distributed, network bandwidth needed for synchronization.
When discussing scalability of the State pattern, start by explaining how state objects grow with instances. Identify memory and CPU as bottlenecks. Then propose sharing and pooling states to save memory, and optimizing transitions to save CPU. Finally, mention distributed state management for very large scale.
Your system uses the State pattern with 1000 QPS on state transitions. Traffic grows 10x. What do you do first?
Answer: First, optimize or cache state transitions to reduce CPU load. Then, implement state sharing or pooling to reduce memory usage before scaling horizontally.
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
