Skip to content

Lifecycle Management & State Transitions

Behaviour changes when internal state changes

Builds on Strategy Pattern: Dynamic Runtime Interchangeability and Enums

Stage 3core6 min read

Behavioral design patterns are about how objects work together and who does which job. They keep that teamwork simple and keep objects loosely coupled, which means each object depends on the others as little as possible.

Imagine a vending machine. If you have put in enough money, it gives you a snack. If you have not, it asks for more. The same button press does different things depending on the machine's state. The State Pattern handles this kind of change.


1. What is the State Pattern?

The State Pattern is a behavioral design pattern that puts the behavior of each state into its own class. The main object passes each request to its current state object (this is called delegating). So the object can change its behavior without its own code being edited.

Formal Definition

The State Pattern is a behavioral design pattern that puts the behavior of each state into its own class and passes each request to the current state object. The object changes its behavior when its internal state changes. From the outside, it looks as if the object changed its class.

Real-Life Analogy: Order Tracking Flow

Consider a food delivery app. An order moves through several stages, and each stage is a state:

  1. The order is placed.
  2. The order is being prepared.
  3. The order is out for delivery.
  4. The order is delivered.

An order can also be cancelled, which is a fifth state. That gives the five states used in the code: Placed, Preparing, OutForDelivery, Delivered and Cancelled.

At each stage, the app behaves differently:

  • In the Order Placed state, you can cancel the order.
  • In the Order Preparing state, you can still cancel the order.
  • In the Out for Delivery state, you can no longer cancel it.

Each state is one phase, and the app behaves differently in each. With the State Pattern, each state class controls the behavior of its own phase and moves the order to the next one. It also follows the Open/Closed Principle (OCP): code should be open to new features but closed to edits of working code. You can add a new state without editing the existing context class.


2. Understanding the Problem: Monolithic Conditionals

Suppose an object stores its state as a plain string and checks that string in many if or switch blocks. Once there are many steps, this code becomes very hard to maintain.

Issues in the Naive Code

  • Rigid State Transition Management: The steps from one state to the next are written inside the nextState() method as a switch statement. Adding a new state means editing that method.
  • Lack of Encapsulation: The Order class itself handles the transition logic and the cancel rules. This violates the Single Responsibility Principle (SRP), which says a class should have one job.
  • Code Duplication Risks: cancelOrder() and nextState() both check the state. Each new action would repeat the same branches.
  • Missing Flexibility for Future Changes: Adding a state or changing a behavior means editing the main Order class each time, and each edit can cause bugs.

3. Class Diagram & Structural Layout

The State Pattern removes the big switch. Each state becomes its own class, and all of them share one interface (a list of methods every state must have). The main object, called the Context, holds the current state object. Polymorphism means the same call, such as next(), does different work depending on which state object is held.

Loading diagram…
ComponentResponsibility
Context (OrderContext)Holds the current state object and gives clients the methods to call.
State Interface (OrderState)Lists the methods every state must have, one for each action an order can take.
Concrete States (OrderPlacedState, etc.)Each one holds the rules for a single stage and changes the context's state when it is time to move on.

4. How the State Pattern Resolves the Issues

IssueSolution with State Pattern
State Transition ManagementEach state class handles its own transitions. You can add a state or change a transition without editing the OrderContext class.
Lack of EncapsulationEach state lives in its own class, so each class has one job (SRP). That is easier to maintain.
Code DuplicationThe behavior of each state sits in that state's class. The main entity no longer repeats the same checks for transitions and cancellations.
Flexibility for Future ChangesTo add a state, write a new class that implements the OrderState interface. Existing code stays untouched. This follows the Open/Closed Principle (OCP).

5. When to Use the State Pattern

  • Behavior Depends on State: When an object must behave differently depending on its current condition or phase.
  • Finite State Configurations: When the states and the moves between them are clear and few.
  • Elimination of Structural Gating: When you want to remove long if-else or switch-case blocks that check a status field to decide what to do.
  • Isolated Operational Inversions: When each state needs its own rules, limits and error messages.

6. Differences Between State and Strategy Pattern

Both patterns work the same way in code. An object holds another object behind an interface and can swap it. Their goals are different:

AspectState PatternStrategy Pattern
Core IntentChanges behavior as the object's own state changes over time.Swaps in a different algorithm at runtime, chosen from outside.
Awareness / DependencyConcrete states usually know the next state and trigger the move to it.Strategies are independent and do not know about each other.
Execution ProfileFollows a lifecycle where the same action gives different results in different phases.Offers different ways to reach the same goal.
Primary Use CasesWorkflow models, transaction pipelines, and complex business lifecycle processors.Dynamic sorting, payment gateway routing, and file compression engines.

7. Trade-Off Analysis

Pros

  • Clear Separation of State Behavior: Each stage has its own class, so you can change one behavior without touching the rest.
  • Extensible Addition of Milestones: A new stage needs very few edits to existing classes.
  • Strict OCP Alignment: New state behavior goes into new files, so existing behavior is unlikely to break (a break in old features is called a regression).
  • Improved Readability: Long switch statements that are hard to follow are gone.

Cons

  • Class Proliferation: Every state needs its own class, so the project has more files.
  • Slightly Complex Initial Setup: You must write the interface, the state classes and the context first. Simple string checks need none of that.
  • Transition Complexity: Transition rules are spread across many state classes. To see the whole flow, you must read several files.

8. Real-World Applications

  1. Swiggy (Food Delivery App): The order lifecycle is a clear example of the State Pattern. After a customer places an order, it moves in turn through Order Placed, Order Preparing, Out for Delivery, and Order Delivered. In the "Placed" state, cancelling is allowed. Once the order is "Out for Delivery", cancelling is blocked and live map tracking starts. Each stage has its own state class.
  2. Uber (Ride-Hailing Platform): Manages ride stages (Ride Requested, Driver Assigned, Ride Accepted, In Progress, Completed). What the app tracks and which actions it allows depend on the state class that is active.
  3. Automated Teller Machines (ATMs): An ATM works differently in each condition (Idle, Card Inserted, PIN Entered, Transaction Processing, Out of Service). The state pattern stops users from doing steps out of order, such as withdrawing cash before the PIN is entered.

In this topic

Done with this topic?

How well do you know it?

PreviousEncapsulating Requests