Skip to main content

🧩 State × Strategy

✅ Combination Intent​

  • Use the State pattern to switch behavior based on the current state
  • Use the Strategy pattern to flexibly change how the behavior is executed

This combination allows separation and flexibility across both state transitions and execution strategy.

✅ Common Use Cases​

  • When behavior changes depending on the current state and the algorithm (strategy) used
  • For example, "Can payment be processed?" + "How should it be processed?"
  • Useful in: payment flows, feature gating by user membership level, reservation logic, etc.

✅ UML Class Diagram​

✅ Code Example​

interface PaymentStrategy {
pay(amount: number): void;
}

class CreditCardPayment implements PaymentStrategy {
pay(amount: number): void {
console.log(`[CreditCard] Paid ${amount}`);
}
}

class PayPalPayment implements PaymentStrategy {
pay(amount: number): void {
console.log(`[PayPal] Paid ${amount}`);
}
}

interface PaymentState {
handle(amount: number): void;
setStrategy(strategy: PaymentStrategy): void;
}

class ReadyState implements PaymentState {
constructor(private strategy: PaymentStrategy) {}

handle(amount: number): void {
console.log("Ready to process payment");
this.strategy.pay(amount);
}

setStrategy(strategy: PaymentStrategy): void {
this.strategy = strategy;
}
}

class DisabledState implements PaymentState {
handle(amount: number): void {
console.log("Payment is currently disabled");
}

setStrategy(strategy: PaymentStrategy): void {
console.log("Cannot set strategy while disabled");
}
}

class PaymentContext {
constructor(private state: PaymentState) {}

setState(state: PaymentState) {
this.state = state;
}

setStrategy(strategy: PaymentStrategy) {
this.state.setStrategy(strategy);
}

pay(amount: number) {
this.state.handle(amount);
}
}

// Usage
const context = new PaymentContext(new ReadyState(new CreditCardPayment()));
context.pay(100);

context.setStrategy(new PayPalPayment());
context.pay(200);

context.setState(new DisabledState());
context.pay(300);

✅ Explanation​

  • PaymentContext holds a reference to the current state (PaymentState) and delegates logic
  • ReadyState and DisabledState implement state-specific behavior via handle()
  • The Strategy (e.g., CreditCardPayment, PayPalPayment) handles the actual processing logic
  • Execution flow:
    • In ReadyState, the context allows the strategy to run
    • In DisabledState, the operation is blocked

✅ Summary​

  • State pattern clearly separates logic based on the object’s state
  • Strategy pattern allows for flexible substitution of processing logic
  • The architecture supports high extensibility and maintainability, as each axis (state and strategy) is decoupled and independently swappable