{} The Go Reference

Behavioral pattern · Gang of Four · Beginner

Strategy

Define a family of interchangeable algorithms, encapsulate each one, and select which to use at runtime.

Also known as — Policy

Behavioral Beginner ⏱ 5 min read Complete

🧭 Analogy

Getting to the airport, you pick a strategy: drive, take the train, or hail a cab. The goal is identical — only the algorithm changes. You choose based on time, budget and traffic, and you can switch on the day without changing your destination.

The problem

A type needs to perform a task that has several interchangeable implementations. The naive approach hard-codes them with a conditional:

func (d *Duck) Fly(kind string) {
	switch kind {
	case "wings":
		// flap...
	case "rocket":
		// ignite...
	case "none":
		// can't fly
	}
}

Every new behavior means editing Fly and growing the switch — the type is closed against extension but you keep opening it. Strategy pulls each algorithm into its own type behind a shared interface, so behavior becomes a value you set, not a branch you add.

Structure

classDiagram
class Duck {
  -flyBehavior FlyBehavior
  +PerformFly()
  +SetFlyBehavior(FlyBehavior)
}
class FlyBehavior {
  <<interface>>
  +Fly()
}
class FlyWithWings { +Fly() }
class FlyNoWay { +Fly() }
Duck o--> FlyBehavior : delegates to
FlyBehavior <|.. FlyWithWings
FlyBehavior <|.. FlyNoWay

The Duck (the context) holds a FlyBehavior and delegates to it. Swapping the concrete behavior changes what the duck does — at runtime, without touching Duck.

How it works

sequenceDiagram
participant C as Client
participant D as Duck (context)
participant F as FlyBehavior
C->>D: SetFlyBehavior(FlyWithWings)
C->>D: PerformFly()
D->>F: Fly()
F-->>D: "Flying with wings"
C->>D: SetFlyBehavior(FlyNoWay)
C->>D: PerformFly()
D->>F: Fly()
F-->>D: "Can't fly"

Idiomatic Go — the interface form

Each behavior is its own type satisfying a tiny interface, and the duck delegates. Edit and Run it:

strategy.go — editable & runnable
package main

import "fmt"

// Strategy interface + two implementations
type FlyBehavior interface{ Fly() }

type FlyWithWings struct{}

func (FlyWithWings) Fly() { fmt.Println("Flying with wings") }

type FlyNoWay struct{}

func (FlyNoWay) Fly() { fmt.Println("Can't fly") }

// Context holds a strategy and delegates to it
type Duck struct{ fly FlyBehavior }

func (d *Duck) SetFly(fb FlyBehavior) { d.fly = fb }
func (d *Duck) PerformFly()           { d.fly.Fly() }

func main() {
mallard := &Duck{fly: FlyWithWings{}}
mallard.PerformFly() // Flying with wings

mallard.SetFly(FlyNoWay{}) // swap behavior at runtime
mallard.PerformFly()       // Can't fly
}

You can extend this with a QuackBehavior too, plus concrete MallardDuck and RubberDuck types that embed a base Duck — a great look at composition.

Idiomatic Go — the function form

Here’s where Go diverges from Java. When a strategy is a single operation, you don’t need an interface and a struct at all — a function value is the strategy:

type FlyFunc func()

type Duck struct{ fly FlyFunc }
func (d *Duck) PerformFly() { d.fly() }

mallard := &Duck{fly: func() { fmt.Println("Flying with wings") }}
mallard.PerformFly()
mallard.fly = func() { fmt.Println("Can't fly") } // swap in one line

🐹 Go reshapes this pattern

First-class functions collapse single-method Strategies into a func field or argument. Reach for the interface form when a strategy needs multiple methods or its own state; reach for the function form when it’s one operation. Both are Strategy.

The principles behind Strategy

Strategy is the Head First Chapter 1 pattern because it distils three of the book’s core principles into one idea:

  • Encapsulate what varies — the fly/quack behavior is the changing part, so it’s pulled into its own interface, away from the stable Duck.
  • Favor composition over inheritance — the duck has-a behavior rather than is-a subclass, so you mix and match at runtime instead of fixing it in a class tree.
  • Program to interfaces, not implementationsDuck depends on FlyBehavior, never a concrete flier.

Its two close cousins are easy to mix up: State has the same shape but the object itself changes its behavior object as it transitions (and states often know each other), whereas a Strategy is chosen from outside and rarely changes itself. Template Method solves a similar “vary the behavior” problem with a fixed skeleton instead of a swappable whole.

In the standard library

  • sort.Slice(s, less) — you supply the comparison strategy as a function.
  • http.HandlerFunc — an adapter that lets any function be a request-handling strategy.
  • strings.Map(mapping, s) — the per-rune transformation is a pluggable strategy.
  • template.FuncMap — inject formatting strategies into a template.

Pitfalls

⚠️ Don't over-engineer a single algorithm

If there’s truly only one implementation and no prospect of a second, an interface and a context type are ceremony with no payoff. Introduce Strategy when variation actually exists (or is clearly coming) — not speculatively.

When to use it — and when not

✅ Reach for it when

  • You have several variants of an algorithm (sorting orders, pricing rules, compression schemes) and want to switch between them at runtime.
  • You want to remove a sprawling if/else or switch that picks behavior.
  • Callers should be able to plug in their own behavior without you editing the core type.

⛔ Think twice when

  • There's only ever one algorithm and no realistic chance of a second — the indirection just adds noise.
  • The 'strategies' differ by a single value, not behavior — a parameter is simpler than a type.

Check your understanding

Score: 0 / 5

1. In Go, what is the most idiomatic form of a simple Strategy?

When a strategy is a single operation, a func value is the cleanest expression — exactly what sort.Slice's less argument is.

2. How does Strategy differ from State?

Both swap behavior behind an interface, but State objects usually decide their own next state, whereas a Strategy is selected from outside and rarely changes itself.

3. Which standard-library API is a Strategy in disguise?

sort.Slice takes the comparison strategy as a function, letting you sort the same data any way you like without changing the sort algorithm.

4. Strategy is the book's first pattern because it showcases three core principles. Which?

The SimUDuck story pulls the varying behavior (fly/quack) out into interfaces, holds them by composition instead of subclassing, and has clients program to the behavior interface — the three principles Strategy is built to teach.

5. Strategy vs Template Method — what's the mechanism difference?

Template Method keeps the algorithm's outline in one place and lets subtypes fill in steps (inheritance/embedding, the Hollywood Principle). Strategy hands the entire algorithm in as an interchangeable object. Composition (Strategy) buys runtime swapping; the skeleton (Template Method) buys a guaranteed shape.

Comments

Sign in with GitHub to join the discussion.