From grimoire
Use when an object's behavior changes based on its internal state and the logic for each state is complex — replacing large conditional chains with state objects where each state encapsulates its own behavior.
How this skill is triggered — by the user, by Claude, or both
Slash command
/grimoire:apply-state-patternThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Allow an object to alter its behavior when its internal state changes — by delegating behavior to state objects rather than using conditional logic.
Allow an object to alter its behavior when its internal state changes — by delegating behavior to state objects rather than using conditional logic.
Adopted by: XState (JavaScript statechart library — 1.5M weekly npm downloads)
implements State pattern as its core model. TCP protocol stack implements state as
objects (CLOSED, LISTEN, SYN_SENT, ESTABLISHED, etc. — each with its own behavior for
each operation). Unity's Animator Controller is a visual state machine based on the
same pattern. Every parser generator (ANTLR, PLY) produces state-based recognizers.
Impact: GoF documents that without the State pattern, a class with 4 states and
6 operations has 4×6=24 conditional branches scattered across all methods. The State
pattern consolidates each state's behavior into one class — 4 state classes of 6 methods
each. Adding a 5th state requires one new class, not editing 6 existing methods.
Why best: if/elif state dispatch — the alternative — scatters state logic across
every method, making it hard to add states or understand what happens in a given state.
State objects make each state's behavior cohesive, isolated, and independently testable.
Sources: Gamma et al. (1994) pp. 305–313; XState documentation; RFC 793 (TCP state machine)
from abc import ABC, abstractmethod
class OrderState(ABC):
@abstractmethod
def pay(self, order: "Order") -> None: ...
@abstractmethod
def ship(self, order: "Order") -> None: ...
@abstractmethod
def cancel(self, order: "Order") -> None: ...
class PendingState(OrderState):
def pay(self, order: "Order") -> None:
print("Payment accepted")
order.state = PaidState()
def ship(self, order: "Order") -> None:
print("Cannot ship — order not paid")
def cancel(self, order: "Order") -> None:
print("Order cancelled")
order.state = CancelledState()
class PaidState(OrderState):
def pay(self, order: "Order") -> None:
print("Already paid")
def ship(self, order: "Order") -> None:
print("Order shipped")
order.state = ShippedState()
def cancel(self, order: "Order") -> None:
print("Cancelling paid order — refund issued")
order.state = CancelledState()
class ShippedState(OrderState):
def pay(self, order: "Order") -> None:
print("Already paid and shipped")
def ship(self, order: "Order") -> None:
print("Already shipped")
def cancel(self, order: "Order") -> None:
print("Cannot cancel — already shipped")
class CancelledState(OrderState):
def pay(self, order: "Order") -> None:
print("Order is cancelled")
def ship(self, order: "Order") -> None:
print("Order is cancelled")
def cancel(self, order: "Order") -> None:
print("Already cancelled")
class Order:
def __init__(self):
self.state: OrderState = PendingState()
def pay(self) -> None:
self.state.pay(self)
def ship(self) -> None:
self.state.ship(self)
def cancel(self) -> None:
self.state.cancel(self)
State transitions (order.state = PaidState()) happen inside state methods (Step 2),
not inside Order. This keeps the context thin and each state's transitions explicit.
def test_pending_pay():
order = Order()
assert isinstance(order.state, PendingState)
order.pay()
assert isinstance(order.state, PaidState)
def test_shipped_cannot_cancel():
order = Order()
order.pay()
order.ship()
order.cancel()
assert isinstance(order.state, ShippedState) # state unchanged
if/else is cleaner than 2 state classes.Context that checks isinstance(state, ...) to make decisions. This reintroduces the conditional logic the pattern eliminates. Delegate the decision to the state object.
State objects that modify context fields directly. If PaidState sets order.paid_at = datetime.now(), the state is coupled to the context's internals. Pass only what state objects need, or use events.
Missing states in the state interface. If one state handles ship() but another doesn't implement the interface method, calls fail unexpectedly. All states must implement all interface methods.
npx claudepluginhub jeffreytse/grimoire --plugin grimoireReplaces conditional logic with state objects that delegate behavior to the current state. Useful for state machines like order lifecycle, connection states, or traffic lights.
Generates State pattern for PHP 8.4 including context, state interface, concrete states, factory, entity updates, exceptions, and unit tests for state-dependent object behavior like order workflows.
Encapsulates interchangeable algorithms in separate classes, enabling runtime swapping without conditional logic. Useful when multiple behaviors exist for a task.