go-patterns
esc

Template Method

Define the skeleton of an algorithm once while letting concrete steps vary through narrow hook methods.

Not read yet ~3 min · 3 files · go1.24
family Behavioral complexity Medium in the wild Medium example src/code/behavioral/template-method

The Problem

Several workflows may share the same broad algorithm but differ in a few steps. Batch jobs, import pipelines, and sync tasks often repeat the same fetch-transform-persist structure. Duplicating the whole algorithm just to vary one stage makes the shared flow harder to maintain.

The Solution

Template Method fixes the high-level sequence while allowing specific steps to vary. In Go, this is often expressed without inheritance by using a runner function plus a small interface of hooks. The overall algorithm stays in one place, and concrete jobs provide only the variable steps.

Structure

Template Method Pattern
Step 1 of 5

The Template Runner

RunSync is the template. It defines the fixed algorithm sequence: FetchRecords → Transform → Persist. The order and the shared error handling logic never change regardless of which job is passed in.

  • Template runner: RunSync defines the fixed algorithm sequence.
  • Hook interface: SyncHooks declares the steps that can vary.
  • Concrete jobs: Inventory and catalog sync jobs provide different fetch and transform logic.
  • Client: Passes a concrete job into the runner.

Implementation

This example runs a sync pipeline with a shared algorithm: fetch records, transform them, and persist the output. Different jobs customize the step behavior without rewriting the pipeline skeleton.

package main

import "fmt"

type SyncHooks interface {
	Name() string
	Fetch() []string
	Transform(records []string) []string
	Persist(records []string) error
}

func RunSync(job SyncHooks) error {
	fmt.Println("running", job.Name())
	records := job.Fetch()
	transformed := job.Transform(records)
	return job.Persist(transformed)
}

Real-World Analogy

Think of a baking recipe. The overall sequence stays fixed: prepare, mix, bake, cool. Different recipes vary the ingredients or timings of specific steps, but they still follow the same skeleton.

Pros and Cons

| Pros | Cons | | --- | --- | | Keeps the shared workflow in one place. | The fixed skeleton can become too rigid as workflows diverge. | | Reduces duplication across similar pipelines. | Hook contracts can become awkward if too many steps vary. | | Lets implementations customize only the steps that vary. | Less flexible than Strategy when the whole algorithm should change. |

Best Practices

  • Keep the fixed algorithm in one place so shared workflow changes happen once.
  • Use a small hook interface that reflects only the steps that vary.
  • In Go, prefer composition and function-driven hooks over inheritance-heavy designs.
  • Reserve Template Method for workflows with a real stable skeleton, not just loosely similar functions.
  • If the algorithm itself should vary, Strategy is usually a better fit.

When to Use

  • Several workflows share the same ordered pipeline but differ in a few steps.
  • You want the common sequence enforced consistently.
  • Concrete jobs should customize behavior without duplicating the full algorithm.

When NOT to Use

  • The steps do not actually follow one stable skeleton.
  • Each workflow varies so much that a shared template becomes brittle.
  • A plain helper function or Strategy would express the reuse more simply.