DEV Community

Sota
Sota

Posted on

Template Method Pattern

What is Template method pattern?

Template method pattern defines the skeleton of an algorithm in a method, deferring some steps to subclasses. Template method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.

When to use it?

  • Use Template method pattern when you have classes that contains similar algorithms with minor differences.

Problem

Let's say we create classes represents animal behavior. Our first version of implementation is below:

Image description

Dog and Bird, they both wake up in the morning and sleep in the night, but eat different things. Our superclass Animal defines common methods for all animals such as wakeUp() and sleep(), but doDailyRoutine() method is declared as abstract and we delegate its implementation to subclasses because it is vary for each animal.

public abstract class Animal {

    public String name;

    public Animal(String name) {
        this.name = name;
    }

    public abstract void doDailyRoutine();

    public void wakeUp() {
        System.out.println(name + " wakes up in the morning.");
    }

    public void sleep() {
        System.out.println(name + " sleeps in the night.");
    }
}
Enter fullscreen mode Exit fullscreen mode
public class Bird extends Animal {

    public Bird(String name) {
        super(name);
    }

    @Override
    public void doDailyRoutine() {
        wakeUp();
        eatFruit();
        sleep();
    }

    public void eatFruit() {
        System.out.println(name + " eats a fruit.");
    }
}
Enter fullscreen mode Exit fullscreen mode
public class Dog extends Animal {

    public Dog(String name) {
        super(name);
    }

    @Override
    public void doDailyRoutine() {
        wakeUp();
        eatDogFood();
        sleep();
    }

    public void eatDogFood() {
        System.out.println(name + " eats dog food.");
    }
}
Enter fullscreen mode Exit fullscreen mode

The problem we have is eatFruit and eatDogFood methods. Despite of different kinds of foods, they both do the same thing, eating. For just one slight difference, we need to write doDailyRoutine method in subclasses over and over. So we should pull up abstraction level to get rid of code duplication.
Also, animals may have their own behavior. We'll see how we can make a room for additional behavior, yet still reduces code duplication.

Solution

Image description

  1. Animal
    Instead of defining doDailyRoutine in subclasses, we define it in superclass Animal. All animals exactly follow the order, that is, wakeUp, eat, sleep. eat method is declared as abstract because each animal eats different things.
    additionalBehavior has empty body, so it does nothing. However, we can override the method in subclasses if some animal have unique behavior. This kind of method is called hook in Template method pattern.

  2. Animal subclasses
    Although animal subclasses can't change algorithm structure, they can override some of the step methods used by doDailyRoutine(). Also, they can optionally add additional behavior by overriding the hook method.

Structure

Image description

Implementation in Java

public abstract class Animal {

    public String name;

    public Animal(String name) {
        this.name = name;
    }

    // TemplateMethod is final to prevent subclasses changes its structure
    public final void doDailyRoutine() {
        wakeUp();
        additionalBehavior();
        eat();
        sleep();
    }

    public void wakeUp() {
        System.out.println(name + " wakes up in the morning.");
    }

    public abstract void eat();

    public void sleep() {
        System.out.println(name + " sleeps in the night.");
    }

    public void additionalBehavior() {
    }
}
Enter fullscreen mode Exit fullscreen mode
public class Dog extends Animal {

    public Dog(String name) {
        super(name);
    }

    @Override
    public void eat() {
        System.out.println(name + " eats dog food.");
    }

    @Override
    public void additionalBehavior() {
        System.out.println(name + " gets cuddled.");
    }
}
Enter fullscreen mode Exit fullscreen mode
public class Bat extends Animal {

    public Bat(String name) {
        super(name);
    }

    @Override
    public void wakeUp() {
        System.out.println(name + " wakes up in the night.");
    }

    @Override
    public void eat() {
        System.out.println(name + " eats insects.");
    }

    @Override
    public void sleep() {
        System.out.println(name + " sleeps in the morning.");
    }
}
Enter fullscreen mode Exit fullscreen mode
public class AnimalTestDrive {

    public static void main(String[] args) {
        Animal bird = new Bird("Pigeon");
        Animal dog = new Dog("Golden retriever");
        Animal bat = new Bat("Silver-haired bat");

        bird.doDailyRoutine();
        System.out.println();
        dog.doDailyRoutine();
        System.out.println();
        bat.doDailyRoutine();
    }
}
Enter fullscreen mode Exit fullscreen mode

Output:

Pigeon wakes up in the morning.
Pigeon sings.
Pigeon eats fruits.
Pigeon sleeps in the night.

Golden retriever wakes up in the morning.
Golden retriever gets cuddled.
Golden retriever eats dog food.
Golden retriever sleeps in the night.

Silver-haired bat wakes up in the night.
Silver-haired bat eats insects.
Silver-haired bat sleeps in the morning.
Enter fullscreen mode Exit fullscreen mode

Pitfalls

  • Algorithm implementations can be spread over multiple subclasses making it hard to maintain.
  • Granularity and flexibility are tradeoff relationship. If AbstractClass have many abstract methods, it offers more flexibility but we'll have more burden in subclasses.

You can check all the design pattern implementations here.
GitHub Repository

Top comments (0)