Back to Design Pattern Course

Design Pattern Course

State is a Behavioral Design Pattern.

State allows an object to alter its behaviour when its internal state changes, making it appear to change its class from the outside. Instead of littering the object's methods with large if/else or switch blocks that check the current state and branch accordingly, each possible state is encapsulated in its own class, and the context object simply delegates to whichever state object is currently active.

The benefit is a clean, open/closed-principle-friendly design: adding a new state doesn't require modifying existing state classes or the context's conditional logic — you just add a new state class.

package com.praveen.designpatterns.behavioral.state; public interface MobileAlertState { void alert(AlertStateContext ctx); }
package com.praveen.designpatterns.behavioral.state; public class Silent implements MobileAlertState { @Override public void alert(AlertStateContext ctx) { System.out.println("silent..."); } }
package com.praveen.designpatterns.behavioral.state; public class Vibration implements MobileAlertState { @Override public void alert(AlertStateContext ctx) { System.out.println("vibration..."); } }
package com.praveen.designpatterns.behavioral.state; public class Ringing implements MobileAlertState { @Override public void alert(AlertStateContext ctx) { System.out.println("ringing..."); } }
package com.praveen.designpatterns.behavioral.state; public class AlertStateContext { private MobileAlertState currentState; public AlertStateContext() { currentState = new Vibration(); // default state } public void setState(MobileAlertState state) { currentState = state; } public void alert() { currentState.alert(this); } }
package com.praveen.designpatterns.behavioral.state; public class StatePatternTest { public static void main(String[] args) { AlertStateContext context = new AlertStateContext(); context.alert(); // vibration context.alert(); // vibration context.setState(new Silent()); context.alert(); // silent context.alert(); // silent context.setState(new Ringing()); context.alert(); // ringing } }

Output

vibration...
vibration...
silent...
silent...
ringing...

A third state (Ringing) is added to better illustrate how the pattern extends cleanly — adding a new state requires no changes to the context class or any existing state class.

One thing worth noting about the example: the AlertStateContext ctx parameter passed to alert() is the pattern's mechanism for allowing state-driven transitions — a state class can call ctx.setState(new SomeOtherState()) to trigger an automatic transition to the next state as a consequence of the operation. In the example here, this parameter isn't used (state transitions are all external/client-driven via setState()). In more realistic scenarios — like a vending machine that transitions from HasMoney → Dispensing → OutOfStock automatically based on what each state operation produces — the state's ability to call back into the context to trigger the next transition is the key mechanism that makes State powerful. Without it, you have essentially the same thing as Strategy.

The compare with the original was well summarised: Strategy decides how to perform an action; State decides when (or what) to perform based on the current lifecycle state of the object.

When to Use the State Design Pattern

  • When an object's behaviour depends on its current state and must change at runtime based on that state.
  • When operations contain large, multi-part conditional logic (if/else chains or switch statements) that branches on the object's state — the State pattern moves each branch into its own class, making each independently testable and changeable.
  • When state transitions are complex or numerous enough that managing them in a single class becomes unwieldy.

Real-World Examples

Beyond the phone alert example, common real-world uses: vending machine states (idle, has money, dispensing, out of stock), traffic light state machine (red → green → yellow → red), TCP connection states (LISTEN, SYN_SENT, ESTABLISHED, CLOSE_WAIT, etc.), order lifecycle states (PLACED → CONFIRMED → SHIPPED → DELIVERED → RETURNED), and document workflow states (DRAFT → IN_REVIEW → APPROVED → PUBLISHED).

State vs Strategy

| | Strategy | State | |---|---|---| | Who controls switching? | Client (external) | Context or state itself (internal) | | Do states know about each other? | No | Often yes (one state transitions to another) | | Purpose | Choose an interchangeable algorithm | Manage object lifecycle/behaviour through states | | Transitions | Client sets the strategy explicitly | Driven by operations/events, often automatic |

State Pattern Examples in the JDK

  • javax.faces.lifecycle.Lifecycle#execute() — JSF's lifecycle moves through defined phases (Restore View → Apply Request Values → Process Validations → Update Model Values → Invoke Application → Render Response), each of which is a distinct state with its own behaviour.
  • java.util.Iterator (conceptually) — hasNext() and next() behave differently depending on the iterator's position state within the underlying collection.