Strategy, State, and Command all end up as an object holding behaviour behind a small interface. Given that near-identical structure, how do you decide which one a problem calls for?
answer
- Strategy: caller picks the algorithm
- State: lifecycle picks, states know successors
- Command: the call itself becomes a storable object
- State variants may reject calls Strategy variants accept
- queue / log / undo ⇒ Command
basics
~20 sBy intent, not shape. Strategy swaps interchangeable algorithms chosen from outside. State encapsulates behaviour that changes with an object's lifecycle stage, and states decide the next state. Command turns a request into an object you can queue, log, retry, or undo.
solid answer
~50 sAll three delegate to a polymorphic object, so structure cannot tell them apart; the discriminators are who chooses the object, whether it changes over time, and what you do with it. Strategy: several interchangeable algorithms for the same task; the client (or configuration) picks one; variants are unaware of each other and typically stateless. State: the object's legal behaviour depends on its current lifecycle stage; transitions happen internally, so states usually know their successors, and the context's behaviour changes as it runs. Command: the *invocation itself* is reified — parameters bound, execution deferred — so it can be queued, scheduled, retried, audited, undone, or sent across a boundary. Practical test: if variants know about each other and swap themselves, it is State; if the object exists to be stored and executed later, it is Command; if it is only about picking one of several ways to compute the same result, it is Strategy. In languages with first-class functions, Strategy and simple Commands collapse into a function or closure.
code
pseudocode · 12 lines// Strategy: caller supplies the algorithm; variants are peers
checkout(cart, pricing: PricingRule) -> pricing.total(cart)
// State: the object holds its stage; the stage decides what is legal and what comes next
order.ship() -> currentState.ship(order)
// Draft.ship(o) = error "not paid"
// Paid.ship(o) = o.state = Shipped
// Command: the invocation is a value you can store, retry, audit, undo
cmd = RefundPayment(orderId, amount)
queue.push(cmd) // executed later, elsewhere
cmd.execute(); cmd.undo()go deeper
Give the one-line intent of each: pluggable algorithm, behaviour that depends on lifecycle stage, request captured as an object. Note the structures look the same.
Use the discriminators: who picks the implementation, does it change over time, do variants know each other, is the object stored for later. Give a concrete example of each.
Add the cost of replacing conditionals with polymorphism, when a plain conditional wins, how State's inter-state coupling is deliberate, and how functions collapse Strategy/Command in modern languages.
Talk about consistency across a codebase — one state-machine idiom rather than three, where transition rules live, how Command shapes reliability (retry, idempotency, audit) and therefore becomes an architectural, not just local, choice.
## Why these three get confused Draw them and you get the same picture: a **context** object holds a reference to an **interface**, and concrete classes implement it. Structure is identical; intent is not. Selection therefore turns on three questions. 1. **Who chooses the concrete object?** 2. **Does the chosen object change during the context's lifetime, and who triggers the change?** 3. **What do you do with the object besides call it once?** ## Strategy **Intent:** define a family of interchangeable algorithms, encapsulate each, and make them substitutable so the algorithm varies independently of the clients that use it. - Chosen from **outside** — by the caller, by configuration, by a feature flag, by dependency injection. - Usually **stateless** and immutable; the same instance can be shared. - Variants are **peers** and do not know each other exists. - Typical smells that lead here: a `switch`/`when` on a mode enum inside a method, repeated in several methods, growing with each new mode. - Examples: pricing rules, compression codecs, routing/eviction policies, retry back-off calculations, sort comparators. ## State **Intent:** allow an object to alter its behaviour when its internal state changes; the object appears to change class. - The variant is chosen by the **object's own lifecycle**, not by the caller. A caller does not "pass in" `Cancelled`. - The set of **legal operations differs per state** — `ship()` is valid in `Paid` and illegal in `Draft`. This is the strongest signal: Strategy variants all accept the same calls and merely compute differently; State variants often reject calls. - **Transitions** are part of the design: either each state names its successors, or a separate transition table does. Either way there is a state machine, which you should be able to draw. - Consequence: states become coupled to each other (or to a shared transition table). That coupling is *the point*, and it is exactly what you do not want in Strategy. - Examples: order/document/subscription lifecycles, TCP-style connection states, media player transport, UI wizard steps. ## Command **Intent:** encapsulate a request as an object, so you can parameterise clients with different requests, queue or log requests, and support undo. - The distinguishing feature is **reification of the invocation**: receiver, parameters, and timing are bundled into a value that can outlive the call site. - You choose Command when you need at least one of: deferred/asynchronous execution, a queue or work list, retry, scheduling, an audit trail, macro/composite operations, or undo/redo (add `undo()` and keep a history stack). - Commands often carry data (the arguments) and therefore have per-instance state, unlike typical Strategies. - Examples: job queue entries, message-broker payload handlers, editor operations with undo, transactional outbox entries, CQRS write commands. ## Fast discriminating questions | Question | Strategy | State | Command | |---|---|---|---| | Who picks the implementation? | Client/config | The object's lifecycle | Client, once, to be run later | | Does it change over the object's life? | Rarely | Yes, that is the point | It is created per request | | Do variants know each other? | No | Usually yes (transitions) | No | | Do variants reject calls that others allow? | No | Often | N/A | | Is the object stored, queued, or replayed? | No | No | Yes | ## Overlaps and combinations - **State + Strategy together:** a state machine whose individual states delegate a computation to a Strategy is perfectly normal. - **Command + Composite:** a `MacroCommand` holding a list of commands is Composite applied to Command. - **Command + Memento:** undo often pairs a Command with a Memento (a captured snapshot) when the inverse operation cannot be derived. - **Strategy + Factory:** something must map input to the right Strategy; that mapping is usually a small factory or a registry keyed by an enum. - **Strategy → policy objects:** when a "strategy" also holds configuration and several related methods, it is drifting toward a policy or a domain service; that is fine, but stop calling it Strategy if variants stop being interchangeable. ## Cost side of the trade All three replace a visible conditional with runtime polymorphism. You gain open/closed extension (new variant = new class, no edits to existing code) and isolated tests. You pay: control flow is no longer readable in one place, stack traces get deeper, and jumping to the implementation requires knowing which instance is bound. For two stable variants that never grow, an `if` is often the better engineering choice. ## Language note With first-class functions, a Strategy is a function value, and a simple Command is a closure — the pattern survives, the class boilerplate does not. State rarely collapses that way, because it needs a per-state *set* of operations and transition logic.
- You have a class with a large switch on an enum. How do you tell whether it should become Strategy or State?Look at what drives the enum. If the caller sets it and it stays put for the object's life, and every branch does the same job differently, it is Strategy. If the enum advances through a lifecycle, if branches assign the next value, or if some branches throw for operations others allow, it is State — and you should be able to draw the transition diagram.
- When is Command overkill compared with just calling the method?When you execute immediately, in-process, once, with no need for retry, audit, scheduling, or undo. Command's value is entirely in what you can do with a deferred, inspectable invocation; without deferral it is pure ceremony.
- Can State and Strategy coexist in one design?Yes. A common shape is a State machine for the lifecycle where one state delegates a varying computation to an injected Strategy — for example a `Pending` state that uses a configurable retry-backoff policy. The two answer different questions, so combining them is not double-patterning.
Strategy is choosing which route app to use; State is a traffic light whose allowed actions depend on which colour it currently shows and which decides its own next colour; Command is writing the trip down on a work order so someone can execute, re-run, or cancel it later.
saying these in an interview costs you the question
- Claiming Strategy and State differ structurally — they are drawn the same; only intent, transitions, and who chooses differ.
- Calling any interface with multiple implementations a Strategy.
- Using State but keeping the transition logic in a giant switch in the context — that is the conditional you were trying to remove.
- Reaching for Command for ordinary synchronous method calls with no deferral, queueing, or undo.
- Assuming Strategy objects should hold mutable per-call state; that breaks sharing and makes them accidentally stateful.