Browse TypeScript Design Patterns & Application Architecture

Defining Algorithm Steps in Template Method Pattern

Explore how to define algorithm steps within the Template Method Pattern using TypeScript, focusing on abstract methods and optional hooks.

6.10.2 Defining Algorithm Steps

In this section, we delve into the intricacies of defining algorithm steps within the Template Method Pattern using TypeScript. This pattern is a powerful tool in the software engineer’s toolkit, allowing for the creation of flexible and reusable code structures. By understanding how to define both mandatory and optional algorithm steps, you can leverage the Template Method Pattern to enforce consistent algorithm structures across subclasses while allowing for customization where necessary.

Understanding the Template Method Pattern

The Template Method Pattern is a behavioral design pattern that defines the skeleton of an algorithm in a base class, allowing subclasses to override specific steps of the algorithm without changing its structure. This pattern is particularly useful when you want to ensure that the overall process remains consistent, but certain parts of the process can vary.

Key Components

  1. Abstract Methods: These are mandatory steps in the algorithm that subclasses must implement. They define the essential parts of the algorithm that need to be customized for each subclass.

  2. Concrete Methods: These are steps that have a default implementation in the base class. Subclasses can choose to override these methods to change their behavior.

  3. Template Method: This is the method in the base class that defines the sequence of steps in the algorithm. It calls both abstract and concrete methods to execute the algorithm.

Defining Algorithm Steps

Abstract Methods: Mandatory Steps

Abstract methods are the backbone of the Template Method Pattern. They represent the steps in the algorithm that are essential and must be implemented by subclasses. By defining these methods as abstract, you enforce a contract that any subclass must fulfill.

Example: Abstract Method in TypeScript
 1abstract class DataProcessor {
 2  // Template method
 3  public process(): void {
 4    this.readData();
 5    this.processData();
 6    this.saveData();
 7  }
 8
 9  // Abstract method: must be implemented by subclasses
10  protected abstract readData(): void;
11
12  // Abstract method: must be implemented by subclasses
13  protected abstract processData(): void;
14
15  // Concrete method with default implementation
16  protected saveData(): void {
17    console.log("Data saved to database.");
18  }
19}
20
21class CsvDataProcessor extends DataProcessor {
22  protected readData(): void {
23    console.log("Reading data from CSV file.");
24  }
25
26  protected processData(): void {
27    console.log("Processing CSV data.");
28  }
29}

In this example, readData and processData are abstract methods that subclasses like CsvDataProcessor must implement. This ensures that every subclass provides its own way of reading and processing data, while the overall process remains consistent.

Concrete Methods: Optional Hooks

Concrete methods in the Template Method Pattern serve as optional hooks. These methods have a default implementation in the base class, but subclasses can override them to provide specific behavior. This flexibility allows for customization without altering the algorithm’s structure.

Example: Concrete Method with Default Implementation
 1abstract class ReportGenerator {
 2  public generateReport(): void {
 3    this.fetchData();
 4    this.formatReport();
 5    this.printReport();
 6  }
 7
 8  protected fetchData(): void {
 9    console.log("Fetching data from default source.");
10  }
11
12  protected abstract formatReport(): void;
13
14  protected printReport(): void {
15    console.log("Printing report.");
16  }
17}
18
19class PdfReportGenerator extends ReportGenerator {
20  protected formatReport(): void {
21    console.log("Formatting report as PDF.");
22  }
23
24  // Optional override of fetchData
25  protected fetchData(): void {
26    console.log("Fetching data from PDF-specific source.");
27  }
28}

Here, fetchData is a concrete method with a default implementation. The PdfReportGenerator subclass can override this method to fetch data from a specific source, demonstrating the flexibility of optional hooks.

Extending and Modifying Behavior

Subclasses can extend or modify behavior by overriding abstract methods and optional hooks. This capability allows developers to tailor the algorithm’s steps to meet specific requirements while maintaining the overall structure defined by the template method.

Benefits of the Template Method Pattern

  1. Code Reuse: By defining the algorithm’s structure in a base class, you can reuse this structure across multiple subclasses, reducing code duplication.

  2. Consistency: The template method ensures that the algorithm’s structure remains consistent, even as subclasses provide different implementations for specific steps.

  3. Flexibility: Optional hooks allow subclasses to customize behavior without altering the core algorithm, providing flexibility where needed.

Considerations for Subclass Implementation

When implementing subclasses, it’s crucial to ensure that they do not violate the intended flow of the algorithm. Here are some considerations to keep in mind:

  1. Adherence to the Template: Subclasses should adhere to the sequence of steps defined by the template method. Avoid altering the order of method calls in the template method.

  2. Override with Caution: While overriding concrete methods is allowed, it should be done with caution to ensure that the algorithm’s integrity is maintained.

  3. Avoiding Side Effects: Ensure that overridden methods do not introduce side effects that could disrupt the algorithm’s flow or produce inconsistent results.

Visualizing the Template Method Pattern

To better understand the Template Method Pattern, let’s visualize the relationship between the base class and its subclasses using a class diagram.

    classDiagram
	    class DataProcessor {
	        +process() void
	        #readData() void
	        #processData() void
	        #saveData() void
	    }
	    class CsvDataProcessor {
	        +readData() void
	        +processData() void
	    }
	    DataProcessor <|-- CsvDataProcessor

This diagram illustrates how CsvDataProcessor inherits from DataProcessor and implements the abstract methods readData and processData.

Try It Yourself

To deepen your understanding of the Template Method Pattern, try modifying the provided examples:

  1. Add a New Subclass: Create a new subclass of DataProcessor that reads and processes data from a JSON file. Implement the readData and processData methods accordingly.

  2. Override a Concrete Method: In the PdfReportGenerator example, try overriding the printReport method to send the report to a network printer instead of printing it locally.

  3. Experiment with Hooks: Add a new optional hook method to the ReportGenerator class, such as validateData, and provide a default implementation. Override this method in a subclass to perform specific validation checks.

References and Further Reading

Knowledge Check

To reinforce your understanding, consider the following questions:

  1. What is the primary purpose of the Template Method Pattern?
  2. How do abstract methods differ from concrete methods in the context of this pattern?
  3. Why is it important to maintain the sequence of steps in the template method?
  4. What are the potential risks of overriding concrete methods in subclasses?

Embrace the Journey

Remember, mastering design patterns is a journey. As you continue to explore and apply these patterns, you’ll gain a deeper understanding of how to create robust and maintainable software. Keep experimenting, stay curious, and enjoy the process!

Quiz Time!

Loading quiz…
Revised on Thursday, April 23, 2026