Template Pattern explained simply
Thilan Dissanayaka Software Architecture January 30, 2020

Template Pattern explained simply

Ever found yourself writing similar logic over and over, only to change a few steps each time? That’s exactly what the Template Pattern helps you solve.

The Template Pattern is a behavioral design pattern that defines the skeleton of an algorithm in a method, deferring some steps to subclasses. It lets you reuse algorithm structure while letting subclasses refine certain steps without changing the overall logic.

What is the Template Pattern?

At its core, the Template Pattern:

  • Defines the outline (template) of an algorithm.
  • Implements the invariant parts (that don’t change).
  • Leaves the changing parts (abstract steps) to be defined by subclasses.

Think of it like a recipe: the steps are fixed, but the ingredients may vary.

Class Diagram

Here’s how the pattern looks structurally. The abstract class holds the template method and the abstract steps, while each concrete class fills in the blanks.

Real-Life Analogy

Imagine you’re making a cup of tea or coffee. The process is almost the same:

  1. Boil water
  2. Brew drink
  3. Pour into cup
  4. Add condiments

The steps are identical, but “brew drink” and “add condiments” differ. The recipe itself is the template – you just swap out the details.

That’s the same idea behind the Template Pattern in code. You lock down the sequence of steps and let subclasses decide what happens inside each one.

Template Pattern in Code (Java)

Let’s use a practical example: a data export pipeline. You have a fixed process – connect, extract, transform, write – but the output format changes depending on whether you want CSV or JSON.

First, the abstract class with the template method:

public abstract class DataExporter {

    // Template method — defines the algorithm skeleton
    public final void export() {
        connectToSource();
        String rawData = extractData();
        String transformed = transformData(rawData);
        writeOutput(transformed);
        System.out.println("Export complete.");
    }

    // Abstract steps — subclasses fill these in
    protected abstract void connectToSource();
    protected abstract String extractData();
    protected abstract String transformData(String data);
    protected abstract void writeOutput(String data);
}

The export() method is final so no subclass can mess with the order of operations. That’s the whole point of the pattern – the structure stays locked.

Now the concrete classes. First, a CSV exporter:

public class CSVExporter extends DataExporter {

    @Override
    protected void connectToSource() {
        System.out.println("Connecting to relational database...");
    }

    @Override
    protected String extractData() {
        System.out.println("Running SQL query...");
        return "id,name,email\n1,Alice,[email protected]\n2,Bob,[email protected]";
    }

    @Override
    protected String transformData(String data) {
        System.out.println("Data already in CSV format, skipping transformation.");
        return data;
    }

    @Override
    protected void writeOutput(String data) {
        System.out.println("Writing to output.csv:\n" + data);
    }
}

And a JSON exporter:

public class JSONExporter extends DataExporter {

    @Override
    protected void connectToSource() {
        System.out.println("Connecting to relational database...");
    }

    @Override
    protected String extractData() {
        System.out.println("Running SQL query...");
        return "id,name,email\n1,Alice,[email protected]\n2,Bob,[email protected]";
    }

    @Override
    protected String transformData(String data) {
        System.out.println("Converting rows to JSON array...");
        // In a real app you'd parse and serialize properly
        return "[{\"id\":1,\"name\":\"Alice\"},{\"id\":2,\"name\":\"Bob\"}]";
    }

    @Override
    protected void writeOutput(String data) {
        System.out.println("Writing to output.json:\n" + data);
    }
}

And here’s how you use them:

public class MainProgram {
    public static void main(String[] args) {
        DataExporter csvExport = new CSVExporter();
        csvExport.export();

        System.out.println();

        DataExporter jsonExport = new JSONExporter();
        jsonExport.export();
    }
}

Output:

Connecting to relational database...
Running SQL query...
Data already in CSV format, skipping transformation.
Writing to output.csv:
id,name,email
1,Alice,[email protected]
2,Bob,[email protected]
Export complete.

Connecting to relational database...
Running SQL query...
Converting rows to JSON array...
Writing to output.json:
[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]
Export complete.

Notice how both exporters follow the exact same four-step sequence. The only thing that changes is what happens inside each step. That’s the template pattern doing its job.

Key Components

  • Abstract Class (DataExporter): Holds the template method and declares abstract steps that subclasses must implement.
  • Template Method (export()): Defines the fixed algorithm structure. Marked final so subclasses can’t override the sequence.
  • Concrete Classes (CSVExporter, JSONExporter): Provide the actual logic for each step – connecting, extracting, transforming, and writing data in their own way.

When to Use the Template Pattern?

  • When multiple classes share the same algorithm structure but differ in individual steps.
  • When you want to avoid code duplication across similar workflows.
  • When you need tight control over the algorithm’s structure while still allowing customization of specific parts.

Advantages

  • Promotes code reuse. You write the algorithm once and never repeat it.
  • Ensures consistent algorithm structure across all implementations.
  • Follows the Hollywood Principle: “Don’t call us, we’ll call you.” The base class controls the flow, not the subclasses.

Disadvantages

  • Requires inheritance, which can reduce flexibility compared to composition-based approaches.
  • Can lead to class explosion if you end up with dozens of subclasses for minor variations.
  • Harder to follow for beginners because the control flow bounces between parent and child classes.

Real-World Use Cases

  • ETL pipelines and report generators: exactly the kind of connect-extract-transform-load workflow we showed above. The pipeline structure stays the same, but each data source or output format gets its own implementation.
  • Frameworks with execution flows: Spring’s AbstractController, Servlet’s HttpServlet (where service() is the template and doGet()/doPost() are the steps you override).
  • Validation and processing pipelines: parse input, validate, process, return result – same skeleton, different rules.
  • Build systems and CI/CD: checkout, compile, test, deploy – fixed order, customizable steps.

Final Thoughts

The Template Pattern is perfect when you have an algorithm with a fixed structure but with parts that vary. It helps you write clean, reusable, and maintainable code.

Next time you find repeated logic that only changes in a few places – reach for the Template Pattern.

ALSO READ
Blockchain 0x000 – Understanding the Fundamentals
May 21, 2020 Web3 Development

Imagine a world where strangers can exchange money, share data, or execute agreements without ever needing to trust a central authority. No banks, no intermediaries, no single point of failure yet...

Identity and Access Management (IAM)
May 11, 2020 Identity & Access Management

Who are you — and what are you allowed to do? That's the fundamental question every secure system must answer. And it's exactly what Identity and Access Management (IAM) is built to solve.

How I built a web based CPU Simulator
May 07, 2020 Pet Projects

As someone passionate about computer engineering, reverse engineering, and system internals, I've always been fascinated by what happens "under the hood" of a computer. This curiosity led me to...

Writing a Shell Code for Linux
Apr 21, 2020 Exploit Development

Shellcode is a small piece of machine code used as the payload in exploit development. In this post, we write Linux shellcode from scratch — starting with a simple exit, building up to spawning a shell, and explaining every decision along the way.

Exploiting a Stack Buffer Overflow on Windows
Apr 12, 2020 Exploit Development

In a previous tutorial we discusses how we can exploit a buffer overflow vulnerability on a Linux machine. I wen through all theories in depth and explained each step. Now today we are going to jump...

Access Control Models
Apr 08, 2020 Identity & Access Management

Access control is one of the most fundamental concepts in security. Every time you set file permissions, assign user roles, or restrict access to a resource, you're implementing some form of access control. But not all access control is created equal...

Exploiting a  Stack Buffer Overflow  on Linux
Apr 01, 2020 Exploit Development

Have you ever wondered how attackers gain control over remote servers? How do they just run some exploit and compromise a computer? If we dive into the actual context, there is no magic happening....

Basic concepts of Cryptography
Mar 01, 2020 Cryptography

Ever notice that little padlock icon in your browser's address bar? That's cryptography working silently in the background, protecting everything you do online. Whether you're sending an email,...

Common Web Application Attacks
Feb 05, 2020 Application Security

Web applications are one of the most targeted surfaces by attackers. This is primarily because they are accessible over the internet, making them exposed and potentially vulnerable. Since these...

Remote Code Execution (RCE)
Jan 02, 2020 Application Security

Remote Code Execution (RCE) is the holy grail of application security vulnerabilities. It allows an attacker to execute arbitrary code on a remote server — and the consequences are as bad as it sounds. In this post, we'll go deep into RCE across multiple languages, including PHP, Java, Python, and Node.js.