193
behavioral OOA

State

Reference Wikipedia ↗
State — state diagram
Plate 193 state diagram

The State pattern allows an object to alter its behavior when its internal state changes. This pattern avoids the use of large conditional statements (like if/else or switch) that often become unwieldy and difficult to maintain when dealing with complex state-dependent logic. Instead, each state is represented by a separate class, leading to a more organized and extensible design.

Essentially, the pattern encapsulates the different states of an object, along with the transitions between those states, into classes. The context object, which represents the object whose behavior changes, delegates the requests to the current state object. This enables the object to seamlessly switch between behaviors based on its state.

Usage

The State pattern is commonly used in scenarios where an object’s behavior is dictated by its state and needs to change dynamically. Some typical use cases include:

  • User Interface (UI): Implementing different states for UI elements like buttons (enabled, disabled, hovered, pressed).
  • Game Development: Defining different states for game characters (idle, walking, running, jumping, attacking).
  • Workflow Management: Modeling different stages in a process, such as order processing (pending, processing, shipped, delivered).
  • Communication Protocols: Representing the different phases of a network connection (listening, connecting, connected, closing).
  • State Machines: More generally, implementing complex state machines where the object transitions between a well-defined set of states based on external events.

Examples

  • TCP Connection: A TCP connection goes through various states (SYN_SENT, ESTABLISHED, FIN_WAIT_1, etc.). Each state handles incoming and outgoing data differently. The TCP protocol itself effectively uses a state machine implemented with concepts similar to the State pattern.

  • Java’s java.util.concurrent.locks.Lock interface & implementations: Specifically, the state management behind acquiring and releasing a lock relies on a pattern similar to State. A lock might be in an ‘unlocked’ state, transition to a ‘locked’ state when acquired, and allow releases only when locked. The internal implementation will handle the state transitions and ensure thread safety.

  • Android Activity Lifecycle: An Android Activity’s lifecycle consists of states like CREATED, STARTED, RESUMED, PAUSED, STOPPED, and DESTROYED. Each state dictates what the activity can and cannot do, and the Android framework internally manages these state transitions.

Specimens

15 implementations
Specimen 193.01 Dart View specimen ↗

The State pattern allows an object to alter its behavior when its internal state changes. It encapsulates each state into separate classes, avoiding the use of conditional statements based on the object’s state. Each state class handles requests differently.

This Dart implementation demonstrates a simple traffic light with three states: Red, Yellow, and Green. The TrafficLight class maintains a currentState object. The setState method allows transitioning between states. Each state (RedState, YellowState, GreenState) implements the TrafficLightState interface, defining how the light handles a next() call, resulting in state changes and appropriate actions (printing the current light). Using interfaces promotes loose coupling and extensibility. This approach utilizes classes and interfaces, aligning with Dart’s strong support for OOP and its emphasis on defining clear contracts through interfaces.

// Define the State interface
abstract class TrafficLightState {
  void next(TrafficLight trafficLight);
}

// Concrete State classes
class RedState implements TrafficLightState {
  @override
  void next(TrafficLight trafficLight) {
    print('Red -> Yellow');
    trafficLight.setState(YellowState());
  }
}

class YellowState implements TrafficLightState {
  @override
  void next(TrafficLight trafficLight) {
    print('Yellow -> Green');
    trafficLight.setState(GreenState());
  }
}

class GreenState implements TrafficLightState {
  @override
  void next(TrafficLight trafficLight) {
    print('Green -> Red');
    trafficLight.setState(RedState());
  }
}

// Context class
class TrafficLight {
  TrafficLightState currentState = RedState();

  void setState(TrafficLightState state) {
    currentState = state;
  }

  void next() {
    currentState.next(this);
  }

  String getCurrentState() {
    if (currentState is RedState) {
      return 'Red';
    } else if (currentState is YellowState) {
      return 'Yellow';
    } else {
      return 'Green';
    }
  }
}

// Example Usage
void main() {
  TrafficLight trafficLight = TrafficLight();
  print('Initial State: ${trafficLight.getCurrentState()}');

  trafficLight.next(); // Red -> Yellow
  print('Current State: ${trafficLight.getCurrentState()}');

  trafficLight.next(); // Yellow -> Green
  print('Current State: ${trafficLight.getCurrentState()}');

  trafficLight.next(); // Green -> Red
  print('Current State: ${trafficLight.getCurrentState()}');
}