Template method pattern in Java and Python Development 14.02.2017

Behavioral Design Patterns provide solution for the better interaction between objects and how to provide lose coupling and flexibility to extend easily. Following design patterns come under this category.

Template method defines the skeleton of how a certain algorithm could be performed, but defers the implementation of those steps to the children classes.

A Template method defines an algorithm in a base class using abstract operations that subclasses override to provide concrete behavior.

The Template method pattern can be used in situations when there is an algorithm, some steps of which could be implemented in multiple different ways. In such scenarios, the Template method pattern suggests keeping the outline of the algorithm in a separate method referred to as a template method inside a class, which may be referred to as a template class, leaving out the specific implementations of the variant portions (steps that can be implemented in multiple different ways) of the algorithm to different subclasses of this class.

The Template method does not necessarily have to leave the implementation to subclasses in its entirety. Instead, as part of providing the outline of the algorithm, the Template class can also provide some amount of implementation that can be considered as invariant across different implementations. It can even provide default implementation for the variant parts, if appropriate. Only specific details will be implemented inside different subclasses. This type of implementation eliminates the need for duplicate code, which means a minimum amount of code to be written.

Intents of Template method are

  • Define the skeleton of an algorithm in an operation, deferring some steps to subclasses.
  • Template method lets subclasses redefine certain steps of an algorithm without letting them to change the algorithm's structure.

Components:

  • AbstractClass. It defines template method defining the structure of algorithm. It also defines abstract operations that will be implemented by subclasses to define steps of algorithm.
  • ConcreteClass. It implements abstract operation of super class to carry out subclass specific steps of the algorithm and also overrides operation if default behavior is not required
dp_template_method.gif

When to use the Template method pattern:

  • To implement the invariant parts of an algorithm once and leave it up to subclasses to implement the behavior that can vary.
  • When common behavior among subclasses should be factored and localized in a common class to avoid code duplication. You first identify the differences in the existing code and then separate the differences into new operations. Finally, you replace the differing code with a template method that calls one of these new operations.

Rules of thumb:

  • Strategy is like Template method except in its granularity. Template method uses inheritance to vary part of an algorithm. Strategy uses delegation to vary the entire algorithm. Strategy modifies the logic of individual objects. Template method modifies the logic of an entire class.
  • Factory method is a specialization of Template method.

Here is an example of the Template method pattern. Meal is an abstract class with a template method called doMeal() that defines the steps involved in a meal. We declare the method as final so that it can't be overridden. The algorithm defined by doMeal() consists of four steps: prepareIngredients(), cook(), eat(), and cleanUp(). The eat() method is implemented although subclasses can override the implementation. The prepareIngredients(), cook(), and cleanUp() methods are are declared abstract so that subclasses need to implement them.

Java

File Meal.java.

public abstract class Meal {
    // template method
    public final void doMeal() {
        prepareIngredients();
        cook();
        eat();
        cleanUp();
    }

    public abstract void prepareIngredients();

    public abstract void cook();

    public void eat() {
        System.out.println("Mmm, that's good");
    }

    public abstract void cleanUp();
}

File HamburgerMeal.java.

public class HamburgerMeal extends Meal {
    @Override
    public void prepareIngredients() {
        System.out.println("Getting burgers, buns, and french fries");
    }

    @Override
    public void cook() {
        System.out.println("Cooking burgers on grill and fries in oven");
    }

    @Override
    public void cleanUp() {
        System.out.println("Throwing away paper plates");
    }
}

File TacoMeal.java.

public class TacoMeal extends Meal {
    @Override
    public void prepareIngredients() {
        System.out.println("Getting ground beef and shells");
    }

    @Override
    public void cook() {
        System.out.println("Cooking ground beef in pan");
    }

    @Override
    public void eat() {
        System.out.println("The tacos are tasty");
    }

    @Override
    public void cleanUp() {
        System.out.println("Doing the dishes");
    }
}

File Demo.java.

public class Demo {
    public static void main(String[] args) {
        Meal meal1 = new HamburgerMeal();
        meal1.doMeal();

        System.out.println();

        Meal meal2 = new TacoMeal();
        meal2.doMeal();
    }
}

Python 3

import abc

class Meal(metaclass=abc.ABCMeta):
    # template method
    def do_meal(self):
        self.prepare_ingredients()
        self.cook()
        self.eat()
        self.clean_up()

    def eat(self):
        print("Mmm, that's good")

    @abc.abstractmethod
    def prepare_ingredients(self):
        pass

    @abc.abstractmethod
    def cook(self):
        pass

    @abc.abstractmethod
    def clean_up(self):
        pass


class HamburgerMeal(Meal):
    def prepare_ingredients(self):
        print("Getting burgers, buns, and french fries")

    def cook(self):
        print("Cooking burgers on grill and fries in oven")

    def clean_up(self):
        print("Throwing away paper plates")


class TacoMeal(Meal):
    def prepare_ingredients(self):
        print("Getting ground beef and shells")

    def cook(self):
        print("Cooking ground beef in pan")

    def eat(self):
        print("The tacos are tasty")

    def clean_up(self):
        print("Doing the dishes")


if __name__ == '__main__':
    meal1 = HamburgerMeal()
    meal1.do_meal()
    print("-----")

    meal2 = TacoMeal()
    meal2.do_meal()
    print("-----")