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 makingpizza
always remains the same but thetoppings
change for each pizza as per requirement. - The basic structure of the
house
always remains the same, astrong foundation
,pillars
, andwalls
withwindows
anddoors
. all types of houses such asapartments
,bungalow
,Farmhouse
,flats
,Hut
,Palace
orpenthouse
, all of them follow the same pattern and add additional functionality as per requirement. Template Design Pattern
majorly used inframeworks
. eachframework
has a predefined order to follow, theimplementation
can be different based on requirements such asRestTemplate
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()
}