Bird
Raised Fist0
LLDsystem_design~25 mins

State pattern in LLD - System Design Exercise

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Design: State Pattern Implementation
Design and implement the state pattern for a single context object with multiple states. Out of scope are concurrency handling, persistence, and UI integration.
Functional Requirements
FR1: Design a system that allows an object to change its behavior when its internal state changes.
FR2: The system should encapsulate state-specific behavior in separate state objects.
FR3: Clients should interact with the context object without knowing the current state.
FR4: Support adding new states without modifying existing code.
FR5: Ensure that state transitions are handled cleanly and maintainably.
Non-Functional Requirements
NFR1: The system should be easy to extend with new states.
NFR2: State transitions should be explicit and controlled.
NFR3: The design should avoid large conditional statements for state behavior.
NFR4: The system should be maintainable and testable.
Think Before You Design
Questions to Ask
❓ Question 1
❓ Question 2
❓ Question 3
❓ Question 4
❓ Question 5
Key Components
Context object holding a reference to a state object
State interface or abstract class defining state-specific methods
Concrete state classes implementing the state interface
State transition logic within states or context
Design Patterns
State pattern for encapsulating state behavior
Strategy pattern for interchangeable behaviors
Factory pattern for creating state instances
Observer pattern if external components need state change notifications
Reference Architecture
  +----------------+
  |    Context     |
  |----------------|
  | - state: State |
  | + request()    |
  +--------+-------+
           |
           v
  +----------------+      +----------------+      +----------------+
  |   State (I)    |<-----| ConcreteStateA |      | ConcreteStateB |
  | + handle()     |      | + handle()     |      | + handle()     |
  +----------------+      +----------------+      +----------------+
Components
Context
Class in any OOP language
Holds current state and delegates requests to it
State Interface
Interface or abstract class
Defines methods for state-specific behavior
Concrete States
Classes implementing State interface
Implement behavior for each specific state and handle transitions
Request Flow
1. Client calls a method on the Context object.
2. Context delegates the call to the current State object.
3. State object executes behavior specific to that state.
4. State object may change the Context's state to another State object.
5. Next client call will be handled by the new State object.
Database Schema
Not applicable for this design pattern as it focuses on object behavior and state management within application memory.
Scaling Discussion
Bottlenecks
Adding many states can increase the number of classes and complexity.
State transitions can become hard to track if scattered across states.
If states share common data, synchronization might be needed in concurrent environments.
Solutions
Use a centralized state transition manager or context to control transitions.
Group related states or use hierarchical state machines to reduce complexity.
Document state transitions clearly and use diagrams for maintainability.
For concurrency, use thread-safe state objects or synchronization mechanisms.
Interview Tips
Time: Spend 10 minutes explaining the problem and requirements, 15 minutes designing the pattern with diagrams and code sketches, and 5 minutes discussing scaling and extensions.
Explain how the State pattern helps avoid large conditional statements.
Show how behavior changes by switching state objects.
Discuss how new states can be added without modifying existing code.
Mention how state transitions are managed and encapsulated.
Highlight maintainability and testability benefits.

Practice

(1/5)
1. What is the main purpose of the State pattern in system design?
easy
A. To provide a global point of access to a resource
B. To create multiple instances of a class efficiently
C. To allow an object to change its behavior when its internal state changes
D. To separate the construction of a complex object from its representation

Solution

  1. 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.
  2. 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.
  3. Final Answer:

    To allow an object to change its behavior when its internal state changes -> Option C
  4. Quick Check:

    State pattern = behavior change by internal state [OK]
Hint: State pattern changes behavior with state, not object creation [OK]
Common Mistakes:
  • Confusing State pattern with Singleton or Builder patterns
  • Thinking it manages object creation instead of behavior
  • Assuming it provides global access to resources
2. Which of the following is the correct way to define a state interface in a typical State pattern implementation?
easy
A. interface State { void handle(); }
B. class State { void handle() {} }
C. enum State { START, STOP }
D. struct State { int status; }

Solution

  1. Step 1: Identify the correct interface syntax

    The State pattern requires a State interface with a method like handle() to define behavior.
  2. 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.
  3. Final Answer:

    interface State { void handle(); } -> Option A
  4. Quick Check:

    State interface defines behavior method [OK]
Hint: State pattern needs interface with behavior method [OK]
Common Mistakes:
  • Using enum or struct instead of interface/class for behavior
  • Defining empty methods without interface
  • Confusing class and interface roles
3. Consider this simplified code snippet using the State pattern:
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?
medium
A. An instance of State
B. An instance of StateB
C. Null (no state)
D. An error occurs

Solution

  1. Step 1: Trace first request()

    Initially, ctx.state = State instance. Calling request() calls State.handle(ctx), which sets state to new StateB.
  2. Step 2: Trace second request()

    Now ctx.state = StateB instance. Calling request() calls StateB.handle(ctx), which sets state back to new State.
  3. Final Answer:

    An instance of State -> Option A
  4. Quick Check:

    State and StateB toggle on requests [OK]
Hint: State and StateB toggle on each request call [OK]
Common Mistakes:
  • Assuming state stays the same after first request
  • Confusing which handle method is called
  • Ignoring state changes inside handle methods
4. In the following State pattern code, what is the main issue causing incorrect behavior?
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();
medium
A. State interface method signature is incorrect
B. Context's state is never updated inside handle, so state never changes
C. Context does not initialize state before request
D. ConcreteStateA does not implement handle method

Solution

  1. Step 1: Analyze state transitions in handle()

    ConcreteStateA.handle() prints a message but does not update Context's state, so no state change occurs.
  2. Step 2: Check other options

    State interface method is correct; Context initializes state before request; ConcreteStateA implements handle properly.
  3. Final Answer:

    Context's state is never updated inside handle, so state never changes -> Option B
  4. Quick Check:

    State transition missing inside handle() [OK]
Hint: State must update Context's state inside handle() [OK]
Common Mistakes:
  • Forgetting to update state inside handle method
  • Assuming printing is enough for state change
  • Ignoring initialization of state before request
5. You are designing a traffic light system using the State pattern. The traffic light cycles through Green, Yellow, and Red states. Which design choice best applies the State pattern to handle state transitions and behavior?
hard
A. Use a global variable to track color and update it externally without encapsulating behavior
B. Use a single class with a variable holding the current color and switch behavior using if-else statements
C. Implement the traffic light as a simple timer without state classes
D. Create separate state classes for Green, Yellow, and Red, each implementing a next() method to switch to the next state

Solution

  1. Step 1: Understand State pattern application

    The pattern suggests encapsulating each state in its own class with behavior and transitions.
  2. Step 2: Evaluate options for scalability and clarity

    Create separate state classes for Green, Yellow, and Red, each implementing a next() 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.
  3. Final Answer:

    Create separate state classes for Green, Yellow, and Red, each implementing a next() method to switch to the next state -> Option D
  4. Quick Check:

    Separate classes with transitions = State pattern [OK]
Hint: Separate states as classes with next() method for transitions [OK]
Common Mistakes:
  • Using if-else instead of separate state classes
  • Not encapsulating state behavior inside classes
  • Relying on external variables without behavior