Open Site Navigation
  • James Duffy

The Template Method Pattern



What is it?

A class behavioral design pattern.


Seriously, what is it?

Templates live among us everywhere. They are a set of instructions that describe how something is done. If you’ve ever tried following a recipe or tutorial, congratulations, you’ve come across the template method pattern!


The Gang of Four describe it as:

The skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithms structure.

Real world examples of the template method could be a routine you have. For example, your morning routine or your exercise routine. The definition of routine lends itself nicely to reinforcing the intent of this pattern:

a sequence of actions regularly followed; a fixed program.

Using an exercise routine as a concrete example we can further explore this pattern.


Example exercise routine

When working out we normally follow 3 steps.

  1. warm up

  2. workout

  3. cool down

If I forget to warm up, I could tear a muscle when working out. If I don’t cool down afterwards, it can cause blood to pool in in the lower extremities, and I faint. So, it’s important that these steps are followed in order. These steps are our template!


Without fainting, let’s say running as an exercise routine should be


1. Warm up

  • Stretching

2. Workout

  • Actual running!

3. Cool down

  • Brisk walking

  • More stretching


We've now defined running in terms of the Template Method Pattern! Now to capture this in code.


The code

There are 2 steps to implementing this pattern

  1. Define the template

  2. Encapsulate what varies

Define the template

You define the template (skeleton) of your method (operation) in an abstract class (since this is a class behavioral pattern).


How about:


typealias Minutes = Int

abstract class ExerciseRoutine(private val warmupTime: Minutes, private val workoutTime: Minutes, private val coolDownTime: Minutes) {
    fun routine(person: Person) {

        warmup(person, warmupTime)

        workout(person, workoutTime)

        coolDown(person, coolDownTime)
    }

    abstract fun warmup(person: Person, time: Minutes)
    abstract fun workout(person: Person, time: Minutes)
    abstract fun coolDown(person: Person, time: Minutes)

}

We’ve defined our template (skeleton) method in terms of a Person. We want to make sure they are warmed up, worked out and cooled down! These are the steps of our template, and never change. The constructor accepts 3 parameters that define how long each step should last (minutes).


Defined below is a simple Person class for this example.

class Person(private val name: String) {
    fun run(time: Minutes){
        println("$name is running for $time minutes!")
    }

    fun briskWalk(time: Minutes){
        println("$name is walking briskly for $time minutes!")
    }

    fun stretch(time: Minutes) {
        println("$name is stretching for $time minutes!")
    }

    fun perform(exercise: ExerciseRoutine) {
        exercise.routine(this)
    }
}

We still need to go for a run though…


Encapsulate what varies - going for a run


class RunningExercise(warmup: Minutes, workout: Minutes, coolDown: Minutes) : ExerciseRoutine(warmup, workout, coolDown) {

    override fun warmup(person: Person, time: Minutes) {
      person.stretch(time)
    }
    
    override fun workout(person: Person, time: Minutes) {
      person.run(time)
    }
    
    override fun coolDown(person: Person, time: Minutes) {
      val walkTime = if(time % 2 != 0) (time + 1) / 2 else time / 2                
      person.briskWalk(walkTime)
        
      val stretchTime = time - walkTime
      person.stretch(stretchTime);
    }
}

The only complexity above is determining how many minutes to walk versus how many to stretch, but that’s not the part to focus on (and I’m sure you can think of a better way than what I did!).


The main thing is our template (skeleton) method routine has defined the steps. How those steps are implemented or performed is delegated to the RunningExercise class. We've ensured that anyone that is performing an exercise routine, must warm up before (no muscle tears, nice!) and cool down afterwards!


We're now demonstrating two key design principles

  • Encapsulate what varies

  • Don’t repeat yourself (DRY)

What varies is what exercise a person is doing (i.e. Running). Developers can add new exercises and only concern themselves with what action is performed. What isn’t duplicated is the order the steps are done in. This is defined in our routine method (the template) and ensures all implementing classes follow this order.


Finally, we can run the above code as:

fun main() {
    val joe = Person("Joe")
    val exercise = RunningExercise(10, 45, 15)
    joe.perform(exercise)
}


Conclusion

I find the Template Method Pattern a good starter for learning about Design Patterns. It is relatively easy to understand yet provides powerful capabilities.


For learning about Design Patterns the two most important books I’ve read (and re-read) are the classic GoF book and Head First series on Design Patterns. I’ve read other ones but I always come back to these two.


In the books, they explain other concepts within the template pattern such as hooks. Where you define a method that a sub class can optionally override.


The possible implementations are as endless as the variations of this pattern you’ll see in the wild!


The takeaway is not just being able to implement a Design Pattern itself, but being able to recognize them within a codebase. In your journey as a developer you may have to navigate a large codebase, or design a solution to some problem. Being able to identify and recognize these patterns reduces your cognitive load and empowers you to implement them when appropriate, hopefully driving you towards a more elegant solution!


References