The Template Method pattern is a behavioral design pattern that defines the skeleton of an algorithm in a base class but lets subclasses override specific steps of the algorithm without changing its structure. This pattern is particularly useful when you have an algorithm with a fixed structure but want to allow for variations in certain steps.

The pattern suggests that we declare the template methods that outline the algorithm’s skeleton in an abstract class. An additional method called the template method calls these methods in the required order. Different algorithm variations can be implemented by extending the abstract class and overriding any methods with different functionality.

The abstract class can have 3 types of methods:

  • Abstract Methods: these methods represent the steps that are different for each algorithm and should be implemented by concrete classes
  • Common Methods: these methods represent the steps that are common for every algorithm and hence are defined in the base abstract class
  • Hook Methods: these methods represent the optional steps having an empty body. These usually provide some additional functionality to the algorithm if required

Structure

The Template Method pattern consists of the following key components:

  • Abstract Class: defines the skeleton of an algorithm. The algorithm itself is broken into several steps, and each step is defined by a method. Apart from these methods, it also defines the actual template method which invokes the steps in the required order according to the algorithm.
  • Concrete Classes: extends the Abstract Class and implements all abstract methods and overrides others if required except the template method

When to use the Template Method Pattern?

  • When you want to introduce new variations for an algorithm in the future (Open/Closed Principle)
  • When you want to separate the algorithm’s structure and its implementation details

Implementation Example

The code below demonstrates the Template Method design pattern for processing different types of documents (PDF and Word) in Java. The abstract DocumentProcessor class defines a template method processDocument() that outlines the algorithm for document processing, including opening the document, extracting text, parsing content, counting words, displaying results, and closing the document. Concrete classes PDFProcessor and WordProcessor extend this abstract class, providing specific implementations for document-type-specific operations. The WordProcessor class overrides the parseContent() method to filter out common words, showcasing how subclasses can customize parts of the algorithm. The Solution class serves as the client, demonstrating how to use both processors with the same method call, resulting in different behaviors based on the document type. This structure allows for a consistent document processing workflow while accommodating the specific needs of different document formats.

import java.util.*;

// Client class
public class Solution {

    public static void main(String[] args) {

        System.out.println("-----Processing PDF Document-----");
        DocumentProcessor pdfProcessor = new PDFProcessor();
        pdfProcessor.processDocument("sample.pdf");

        System.out.println("\n-----Processing Word Document-----");
        DocumentProcessor wordProcessor = new WordProcessor();
        wordProcessor.processDocument("sample.docx");
    }
}

// Abstract Class
abstract class DocumentProcessor {

    // Template method
    public final void processDocument(String filePath) {

        openDocument(filePath);
        String content = extractText();
        List<String> words = parseContent(content);
        int wordCount = countWords(words);
        displayResults(wordCount);
        closeDocument();
    }

    // Abstract Methods
    protected abstract void openDocument(String filePath);
    protected abstract String extractText();
    protected abstract void closeDocument();

    // Common Methods
    protected List<String> parseContent(String content) {

        System.out.println("Parsing content into words...");

        return Arrays.asList(content.split("\\s+"));
    }

    protected int countWords(List<String> words) {

        System.out.println("Counting words...");
        return words.size();
    }

    protected void displayResults(int wordCount) {

        System.out.println("Total words: " + wordCount);
    }
}

// Concrete Classes
class PDFProcessor extends DocumentProcessor {

    protected void openDocument(String filePath) {

        System.out.println("Opening PDF document: " + filePath);
    }

    protected String extractText() {

        String contents = "This is a sample PDF content with some text to process.";
        System.out.println("Extracting text from PDF document...");
        System.out.println("Contents of PDF document: " + contents);

        return contents;
    }

    protected void closeDocument() {

        System.out.println("Closing PDF document...");
    }
}

class WordProcessor extends DocumentProcessor {

    protected void openDocument(String filePath) {

        System.out.println("Opening Word document: " + filePath);
    }

    protected String extractText() {

        String contents = "This is a sample Word document content with some more text to process.";
        System.out.println("Extracting text from Word document...");
        System.out.println("Contents of Word document: " + contents);

        return contents;
    }

    protected void closeDocument() {

        System.out.println("Closing Word document...");
    }

    protected List<String> parseContent(String content) {

        System.out.println("Parsing content into words (ignoring 'a' and 'the')");
        
        List<String> allWords = super.parseContent(content);
        List<String> filteredWords = new ArrayList<>();
        
        for (String word : allWords) {

            if (!word.equalsIgnoreCase("a") && !word.equalsIgnoreCase("the")) {
                filteredWords.add(word);
            }
        }

        return filteredWords;
    }
}

The output of the following program will be:

-----Processing PDF Document-----
Opening PDF document: sample.pdf
Extracting text from PDF document...
Contents of PDF document: This is a sample PDF content with some text to process.
Parsing content into words...
Counting words...
Total words: 11
Closing PDF document...

-----Processing Word Document-----
Opening Word document: sample.docx
Extracting text from Word document...
Contents of Word document: This is a sample Word document content with some more text to process.
Parsing content into words (ignoring 'a' and 'the')
Parsing content into words...
Counting words...
Total words: 12
Closing Word document...