Template Design Pattern

The Template Design Pattern is one of the simplest Template Pattern, it comes under Behavioural Design Pattern.
Algorithms are steps to perform a task that needs to be performed in an orderly manner, the different entities might have slight changes in execution, but the flow always remains the same.

Template design pattern defines the skeleton of the algorithm with basic definitions with placeholders and lets the implementing class override or define new as per requirement but cannot change the execution flow. it moves the common functionality to a common class to avoid duplication and reusing the code.

It defines the steps to execute in a template with basic definitions or common code also known as Hooks and left the rest for the implementing class to override the existing implementation or define new functionality as per the requirement, but the flow of algorithm execution will always remain the same as defined in the parent class.

Generally a superclass calls the method called from subclasses but in Template Design Pattern, the superclass template method calls methods of subclasses, this is a very famous Hollywood Principle ie don’t call us, we’ll call you.

Some real-world examples of Template Design Pattern are

  • Everyone loves pizza, and the process of making pizza always remains the same but the toppings change for each pizza as per requirement.
  • The basic structure of the house always remains the same, a strong foundation, pillars, and walls with windows and doors. all types of houses such as apartments, bungalow, Farmhouse, flats, Hut, Palace or penthouse, all of them follow the same pattern and add additional functionality as per requirement.
  • Template Design Pattern majorly used in frameworks. each framework has a predefined order to follow, the implementation can be different based on requirements such as RestTemplate
package org.wesome.design.patterns;

import lombok.AllArgsConstructor;

import java.util.ArrayList;
import java.util.List;

@AllArgsConstructor
public abstract class Salad {
    public static final String STEP_4 = "Pinch of salt and a splash of lemon juice.";
    private boolean chopped;

    public abstract String cutInPieces();

    public abstract String addCheese();

    public abstract String addOil();

    /*  Template Design Pattern can also provide default implementation, subclasses are free to use existing or override as per requirement    */
    public String sprinkleSeasoning() {
        return STEP_4;
    }

    /*  defining the flow of execution, implementing class cannot override or change the flow   */
    public final List<String> prepareSalad() {
        List<String> steps = new ArrayList<>();
        /*  based on conditions, flow can be changed   */
        if (!chopped) {
            steps.add(cutInPieces());
        } else {
            System.out.println("Apple is already cut is pieces, hence ignoring this step");
        }
        steps.add(addCheese());
        steps.add(addOil());
        steps.add(sprinkleSeasoning());
        return steps;
    }
}
package org.wesome.design.patterns;

public class Fuji extends Salad {
    public static final String STEP_1 = "Cut fuji apple in square.";
    public static final String STEP_2 = "Add mozzarella cheese to the apple.";
    public static final String STEP_3 = "Toss in olive oil.";
    public static final String STEP_4 = "Pinch of truffle salt and a splash of lemon juice.";

    public Fuji(boolean chopped) {
        super(chopped);
    }

    @Override
    public String cutInPieces() {
        return STEP_1;
    }

    @Override
    public String addCheese() {
        return STEP_2;
    }

    @Override
    public String addOil() {
        return STEP_3;
    }

    /*  Fuji class utilizing the existing defined implementation */
    @Override
    public String sprinkleSeasoning() {
        return STEP_4;
    }
}
package org.wesome.design.patterns;

public class Macintosh extends Salad {
    public static final String STEP_1 = "Cut Macintosh apple in long rectangular sticks.";
    public static final String STEP_2 = "Add cheddar cheese to the apple.";
    public static final String STEP_3 = "Toss in coconut oil.";

    public Macintosh(boolean chopped) {
        super(chopped);
    }

    @Override
    public String cutInPieces() {
        return STEP_1;
    }

    @Override
    public String addCheese() {
        return STEP_2;
    }

    @Override
    public String addOil() {
        return STEP_3;
    }

    /*  Macintosh class utilizing the existing defined implementation */
}
package org.wesome.design.patterns;

public class Template {
    public static void main(String[] args) {
        Salad fujiSalad = new Fuji(false);
        fujiSalad.prepareSalad().forEach(System.out::println);
        Salad macintoshSalad = new Macintosh(true);
        macintoshSalad.prepareSalad().forEach(System.out::println);
    }
}
package org.wesome.design.patterns;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;

public class TemplateTest {
    @Test
    void testMacintoshTemplate() {
        Salad macintoshSalad = new Macintosh(true);
        List<String> actualSteps = macintoshSalad.prepareSalad();
        List<String> expectedSteps = Arrays.asList(Macintosh.STEP_2, Macintosh.STEP_3, Salad.STEP_4);
        Assertions.assertEquals(expectedSteps, actualSteps);
    }

    @Test
    void testFujiTemplate() {
        Salad fujiSalad = new Fuji(false);
        List<String> actualSteps = fujiSalad.prepareSalad();
        List<String> expectedSteps = Arrays.asList(Fuji.STEP_1, Fuji.STEP_2, Fuji.STEP_3, Fuji.STEP_4);
        Assertions.assertEquals(expectedSteps, actualSteps);
    }
}
plugins {
    id 'java'
    id "io.freefair.lombok" version "6.2.0"
}

group = 'org.wesome'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}
repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.junit.jupiter:junit-jupiter:5.6.2'
}

test {
    useJUnitPlatform()
}

 

follow us on