Library
9 min read

Strategy vs Template Method vs Command

Published

All three wrap a piece of behaviour so it can be plugged in somewhere, which is why they get lumped together. Split them into two pairs and they stop blurring.

Strategy vs Template Method is one problem solved two ways. A job has a fixed outline and one step that varies. Template Method puts the outline in a parent class and lets a child class fill in the step: the franchise kitchen manual from abstraction vs interface vs abstract class, where steps one to three are fixed and step four is "your local special". Strategy hands the varying step in as a separate object: choosing car, bike or walk in Maps.

Strategy vs Command is a different question. A strategy is how to do a job. A command is what to do, captured as a thing: the order slip from the LLD course. The waiter writes "table 4, two dosas" on a slip, and that slip can wait in a queue, be read by the kitchen later, be crossed out, or be kept for the bill.

Your answer

You add an Undo button to the cart. Separately, the cart total can be worked out with a regular, a weekend or a combo pricing rule. Which of these is a Command and which is a Strategy? What does one of them have to remember that the other never does?

Strategy vs Template Method: composition or inheritance#

Here is one job, exporting order rows with a header line, where only the row format varies. First as a template method:

ts
abstract class Exporter {
  // The outline is fixed here. Children only fill in row().
  export(rows: string[][]) {
    return ['# orders', ...rows.map((r) => this.row(r))]
  }
  protected abstract row(cells: string[]): string
}
 
class CsvExporter extends Exporter {
  protected row(cells: string[]) {
    return cells.join(',')
  }
}
 
const rows = [['A17', '120'], ['A18', '40']]
for (const line of new CsvExporter().export(rows)) console.log(line)
// > # orders
// > A17,120
// > A18,40

And the same job as a strategy, where the varying step is a function passed in:

ts
type RowFormat = (cells: string[]) => string
 
function exportReport(rows: string[][], row: RowFormat) {
  return ['# orders', ...rows.map(row)]
}
 
const csv: RowFormat = (cells) => cells.join(',')
const pipes: RowFormat = (cells) => cells.join(' | ')
 
const rows = [['A17', '120'], ['A18', '40']]
console.log(exportReport(rows, csv)) // [ '# orders', 'A17,120', 'A18,40' ]
console.log(exportReport(rows, pipes)[1]) // A17 | 120

Same result. The differences show up later, as the code grows:

  • Switching at runtime. A strategy is a value, so the user can pick CSV or pipes from a dropdown. With template method the choice is a class, made when the object is created.
  • Two varying steps. Say the header also varies, three ways. Template method needs a subclass per combination (3 row formats × 3 headers = 9 classes). Strategy needs 3 + 3 small functions, combined freely.
  • Testing. A strategy function is tested by calling it. A template method's step is protected and only reachable through a subclass.

That is the reasoning behind "favour composition over inheritance". Template method is still a good fit when the outline really is fixed and owned by a framework: test runners calling beforeEach, the test, then afterEach, or React class components where React calls render and componentDidMount in an order you do not control.

Strategy vs Command: how, or what#

Now the other pair. Play with the cart below: every button creates a command, and the pricing rule is a strategy. Then press Undo.

What lands in the history
Commands: what to do
Strategy: how to price
Cart: empty
₹0 → total ₹0 (subtotal)
History, newest last
  1. Nothing yet.
Add a few items, remove one, switch the pricing rule, then press Undo. Watch what goes into the history and what does not.

The pricing rule changes how the total is worked out. Switching it three times leaves no trace, because nothing happened to the cart. Each command is a record of something that happened, with everything needed to do it again or take it back. So commands can go into a history, and strategies cannot.

ts
interface Command {
  execute(): void
  undo(): void
}
 
class Cart {
  items: string[] = []
}
 
class AddItem implements Command {
  constructor(private cart: Cart, private item: string) {}
  execute() {
    this.cart.items.push(this.item)
  }
  undo() {
    this.cart.items.pop()
  }
}
 
class RemoveFirst implements Command {
  private removed?: string
  constructor(private cart: Cart) {}
  execute() {
    this.removed = this.cart.items.shift()
  }
  undo() {
    if (this.removed) this.cart.items.unshift(this.removed)
  }
}
 
class History {
  private done: Command[] = []
  run(cmd: Command) {
    cmd.execute()
    this.done.push(cmd)
  }
  undo() {
    this.done.pop()?.undo()
  }
}
 
const cart = new Cart()
const history = new History()
history.run(new AddItem(cart, 'Masala dosa'))
history.run(new AddItem(cart, 'Filter coffee'))
history.run(new RemoveFirst(cart))
console.log(cart.items) // [ 'Filter coffee' ]
history.undo()
console.log(cart.items) // [ 'Masala dosa', 'Filter coffee' ]

Notice what RemoveFirst keeps: the item it removed. A command remembers enough about this one time it ran to reverse it. A strategy never remembers anything between calls. You hand it data, it hands back an answer.

That ability to treat an action as a value is why commands show up everywhere once you know the shape:

  • Undo and redo: editors, design tools, the cart above.
  • Queues and jobs: a background job is a command written down as data so another process can run it later, and retry it.
  • Redux actions: { type: 'cart/addItem', item: 'Masala dosa' } is a command as plain data. The reducer is what executes it.
  • Audit logs: keep every command and you have a history of who did what.

All three side by side#

StrategyTemplate MethodCommand
AnswersHow to do this jobWhich step differs in a fixed outlineWhat to do, as a thing
Built withComposition: an object or function passed inInheritance: a child overrides a stepAn object holding an action and its arguments
ChosenAt runtime, by the callerWhen the class is pickedEach time something happens
Remembers anythingNoNoYes, enough to undo or replay
Can be queued, logged, undoneNoNoYes
Lighter TS formA functionA function parameter (which makes it Strategy)A plain data object, or a pair of functions
Isn't a command just a function?

A command with no undo, run straight away, is just a callback, and in TypeScript you would pass () => void. It becomes the Command pattern when you need the action as a value with a life of its own: stored, sent to another process, run later, retried, or reversed. That is when an object with execute and undo (or a data shape like a Redux action) earns its keep.

ChoiceWhat you gainWhat you payPick it when
StrategySwap the algorithm at runtime, combine several varying steps, test each one alone.The caller must know the options and pass one in.Interchangeable ways to do one job: pricing, sorting, formatting, rider assignment.
Template MethodThe outline is enforced in one place and children cannot reorder it.One subclass per variation, combinations multiply, and children depend on the parent's internals.A framework that owns the sequence and gives you hooks to fill in.
CommandActions become values: undo, redo, queues, retries, audit history.A class or data type per action, and undo logic that must stay correct.You need to store, delay, send, repeat or reverse what was done.
TypeScript you just picked up
protected abstract row(cells: string[]): string
The hole in a template method: every child must fill it, and only the class and its children can call it.
string[][]
An array of arrays of strings, like rows of cells.
['# orders', ...rows.map(row)]
Spread: put every element of the mapped array after the first item.
private removed?: string
An optional field: it starts undefined and is set when the command runs.
this.done.pop()?.undo()
Optional chaining: call undo only if pop returned something. On an empty history it does nothing.

In the interview#

Interviewer
When would you use Template Method instead of Strategy?
You
They solve the same problem: a fixed outline with one step that varies. Template method does it with inheritance, so the parent owns the sequence and a subclass overrides the step. Strategy does it with composition, passing the step in. I default to strategy, often just a function, because I can switch it at runtime, combine several varying steps without a subclass per combination, and test each piece alone. Template method fits when a framework owns the sequence, like test runners with before and after hooks.
Names the shared problem first, then composition versus inheritance and a default.
Interviewer
And how is Command different from Strategy? Both wrap a function in an object.
You
A strategy is how to do a job and remembers nothing; I call it with data and get an answer. A command is a specific action with its arguments, captured so it can be stored, queued, retried or undone. The test is whether I would ever want to keep it in a list. Undo history, job queues and Redux actions are commands. A pricing rule is a strategy.
Which one?
An image editor where every brush stroke can be undone
A payment base class whose process() always validates, calls charge(), then writes an audit line, with charge() left to subclasses
Array.prototype.sort((a, b) => a.price - b.price)
A "send invoice email" job written to a queue for a worker to run within five minutes

Checkpoint#

Checkpoint

1. What is the main structural difference between Strategy and Template Method?

2. Why can a command be undone when a strategy cannot be?

3. A Redux action like { type: "cart/addItem", item: "Vada" } is closest to which pattern?

Worth memorising
  • Strategy and Template Method: same problem. Composition (pass it in) vs inheritance (override it).
  • Default to Strategy, often a plain function. Template method for framework owned outlines.
  • Strategy is how. Command is what, as a thing.
  • A command remembers enough to undo or replay itself. A strategy remembers nothing.
  • The test: would you ever keep it in a list? Then it is a command.
Say this in 60 seconds

These three put behaviour in a slot, and I split them into two pairs. Strategy and Template Method solve the same problem, a fixed outline with a step that varies. Template method uses inheritance: the parent owns the sequence and a subclass overrides the step. Strategy uses composition: the step is passed in, often as a plain function, so it can be switched at runtime, combined with other varying steps without a subclass per combination, and tested alone. I default to strategy and use template method when a framework owns the outline. Strategy versus Command is how versus what. A strategy is a reusable way to compute something and remembers nothing. A command is one specific action with its arguments, captured as a value, so it can be stored in an undo history, put on a job queue, retried or logged. If I would ever keep it in a list, it is a command.