Behavioral Complexity: Medium

Template Method in Go

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

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 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)
}

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.