How would you implement a template method pattern using an abstract class in C?

Question

How would you implement a template method pattern using an abstract class in C?

Brief Answer

Brief Answer: Template Method Pattern (C++ Context)

The Template Method is a behavioral design pattern that defines the skeleton of an algorithm in a superclass, allowing subclasses to override specific steps without changing the algorithm’s overall structure.

Important Note on C vs. C++: While the question mentions “C”, the Template Method pattern fundamentally relies on Object-Oriented Programming (OOP) concepts like abstract classes, inheritance, and polymorphism, which are not natively supported in standard C. It’s best implemented in OOP languages like C++.

Implementation in C++:
1. Abstract Base Class: Define an abstract class that serves as the blueprint for the algorithm.
2. Template Method (Concrete): This method is concrete (non-pure virtual) and resides in the base class. It defines the fixed sequence of steps, orchestrating the entire algorithm’s flow by calling other methods.
3. Pure Virtual Methods (Abstract Steps): These are declared as `virtual void method() = 0;` in the base class. They represent the customizable parts of the algorithm that must be implemented by concrete subclasses.
4. Concrete Methods (Common Steps): These methods are fully implemented directly in the base class for steps common to all variations of the algorithm.
5. Hook Methods (Optional Steps): Optional virtual methods with a default (often empty or basic) implementation that subclasses can override if they need to inject specific logic at a certain point, providing flexibility without forcing implementation.

Key Benefits:
* Code Reuse: Common logic is implemented once in the base class, reducing duplication.
* Enforces Consistency: Guarantees all algorithm variations follow the same high-level sequence.
* Extensibility: New variations are easily added by creating new subclasses that implement the required abstract methods.
* Inversion of Control: The base class (the template method) controls the overall flow, calling subclasses for specific parts.

Template Method vs. Strategy:
* Template Method: Uses inheritance to vary *parts* of an algorithm at compile-time. The overall algorithm structure is fixed.
* Strategy: Uses composition to vary an *entire* algorithm or a significant part of it at runtime. Provides more dynamic flexibility by swapping different strategy objects.

Super Brief Answer

Super Brief Answer: Template Method Pattern

The Template Method pattern defines an algorithm’s skeleton in a superclass, allowing subclasses to customize specific steps without altering the overall structure.

Context: This pattern relies on OOP concepts (abstract classes, polymorphism) and is primarily implemented in languages like C++, not standard C.

Mechanism in C++: An abstract base class contains a concrete “template method” that orchestrates the algorithm’s fixed sequence. This template method calls both concrete methods (common steps) and pure virtual methods (customizable steps that subclasses *must* implement).

Benefit: Promotes code reuse and enforces consistent algorithm structure while enabling necessary customization.

Detailed Answer

The Template Method design pattern is a behavioral design pattern that defines the skeleton of an algorithm in the superclass but lets subclasses override specific steps of the algorithm without changing its structure.

Note on Language Context: While the original question mentions “C”, the concept of “abstract classes” and the direct application of the Template Method pattern as described (involving inheritance and polymorphism) are fundamental to object-oriented programming (OOP) languages like C++ or C#. Standard C, being a procedural language, does not have built-in support for abstract classes in the OOP sense. This answer will therefore explain and illustrate the Template Method pattern within the context of C++, which is a common language for discussing design patterns and directly supports abstract classes.

Implementing the Template Method Pattern in C++

To implement the Template Method pattern using an abstract class in C++, you define an abstract base class. This class contains a template method that outlines the algorithm’s skeleton. Some steps of this algorithm are implemented directly within the abstract class (concrete methods), while others are declared as pure virtual methods (abstract methods), requiring subclasses to provide their specific implementations. This approach ensures a consistent algorithm structure while enabling customization.

Key Principles

  • The Abstract Class Defines the Algorithm Structure

    The abstract class acts as a blueprint, defining the overall skeletal structure of the algorithm. The template method within this class orchestrates the execution flow, calling both concrete methods (common steps) and pure virtual methods (customizable steps) in a specific, predefined order. This ensures a consistent structure across all concrete implementations.

  • Abstract Methods Represent Customizable Steps

    Pure virtual methods in the abstract class serve as placeholders for steps that must be customized by subclasses. Each concrete subclass provides its own unique implementation for these methods, tailoring the algorithm’s specific parts to its needs while adhering to the overall flow defined by the template method.

  • The Template Method Itself is Concrete

    The template method itself is typically a non-pure virtual (concrete) method. It provides the overarching logic of the algorithm and calls the various steps, whether they are concrete methods in the base class, pure virtual methods, or optional “hook” methods that subclasses can override.

Benefits of the Template Method Pattern

The Template Method pattern offers several significant advantages:

  • Code Reuse: Common parts of the algorithm are implemented once in the abstract base class, reducing duplication across different concrete implementations.
  • Enforces Consistent Structure: It guarantees that all variations of the algorithm follow the same high-level sequence of steps, promoting uniformity and maintainability.
  • Extensibility: New variations of the algorithm can be introduced simply by creating new subclasses that implement the required abstract methods, without modifying the base class.
  • Inversion of Control: The base class (the template method) controls the overall flow, and subclasses are “called back” to provide specific implementations for certain steps.

Real-World Examples

The Template Method pattern is widely applicable in scenarios where different implementations share a common high-level structure:

  • Order Processing System

    Consider an order processing system. The overall process (e.g., validateOrder, processPayment, shipOrder) remains consistent, but the processPayment step might differ significantly for online orders versus in-store orders. The template method defines the overall flow, while concrete subclasses (e.g., OnlineOrderProcessor, InStoreOrderProcessor) provide specific payment logic.

  • Reporting System

    Imagine developing a reporting system with various report types (PDF, Excel, CSV). Generating each report involves common steps like data retrieval and formatting, but the final output generation differs. Using the Template Method pattern, an abstract ReportGenerator class can define a template method handling common steps. Subclasses like PdfReportGenerator and ExcelReportGenerator would then only implement the output-specific logic, significantly reducing code duplication and ensuring a consistent report generation process.

  • Game AI (Enemy Behavior)

    In a game with different enemy types, each with unique attack strategies, the Template Method pattern can be invaluable. An abstract Enemy class could define a template method Attack() that outlines the basic attack sequence (e.g., approach, attack, retreat). Subclasses like Orc and Dragon would then implement their specific attack logic within the Attack() method, ensuring all enemies follow the same basic attack structure while having unique behaviors.

Template Method vs. Strategy Pattern

While both the Template Method and Strategy patterns address algorithm customization, they differ in their approach and the design principle they emphasize:

  • Template Method: Uses inheritance to vary parts of an algorithm. The overall algorithm structure is fixed in the base class, and subclasses override specific steps. This is a compile-time decision.
  • Strategy Pattern: Uses composition to vary an entire algorithm or a significant part of it. Different “strategy” objects can be swapped out at runtime, changing the behavior dynamically. This promotes greater flexibility.

For example, if you need to change the payment processing logic at runtime, the Strategy pattern, using composition, would be more suitable. You could swap payment strategies dynamically without altering the core order processing logic. However, the Strategy pattern can lead to a proliferation of small strategy classes. In cases where the core algorithm structure is relatively fixed and customization occurs at specific, predefined points, the Template Method pattern often provides a cleaner, more straightforward solution through inheritance.

C++ Code Example

Here’s a C++ example demonstrating the Template Method pattern using an abstract OrderProcessor class.


#include <iostream>
#include <string>

// Abstract Base Class: Defines the template method and primitive operations
class OrderProcessor {
public:
    // The Template Method: Defines the overall algorithm skeleton
    // It is concrete and orchestrates the steps.
    void processOrder() {
        validateOrder();      // Common step, implemented in base class
        processPayment();     // Abstract step, must be implemented by subclasses
        shipOrder();          // Common step, implemented in base class
        logCompletion();      // Hook method, can be overridden by subclasses
    }

    // Pure virtual method (abstract step): Must be implemented by concrete subclasses.
    // Represents the customizable part of the algorithm.
    virtual void processPayment() = 0;

    // Concrete method (common step): Implemented in the base class.
    // Represents a fixed step in the algorithm.
    void validateOrder() {
        std::cout << "Step 1: Validating order details." << std::endl;
    }

    // Concrete method (common step): Implemented in the base class.
    // Another fixed step.
    void shipOrder() {
        std::cout << "Step 3: Shipping order." << std::endl;
    }

    // Hook method (optional step): Can be overridden by subclasses, but has a default implementation.
    virtual void logCompletion() {
        std::cout << "Step 4: Order processing completed (default log)." << std::endl;
    }

    // Virtual destructor is crucial for proper cleanup when dealing with polymorphism
    // and deleting derived class objects through a base class pointer.
    virtual ~OrderProcessor() = default;
};

// Concrete Subclass for Online Orders
class OnlineOrderProcessor : public OrderProcessor {
public:
    // Implements the abstract processPayment method for online specifics.
    void processPayment() override {
        std::cout << "Step 2: Processing online payment via secure gateway." << std::endl;
    }

    // Overrides the hook method for specific online order logging.
    void logCompletion() override {
        std::cout << "Step 4 (Online): Online order processing complete and email notification sent." << std::endl;
    }
};

// Concrete Subclass for In-Store Orders
class InStoreOrderProcessor : public OrderProcessor {
public:
    // Implements the abstract processPayment method for in-store specifics.
    void processPayment() override {
        std::cout << "Step 2: Processing in-store payment (cash/card at POS)." << std::endl;
    }
    // The logCompletion() hook method is not overridden, so the base class's default
    // implementation will be used for in-store orders.
};

int main() {
    std::cout << "--- Processing Online Order ---" << std::endl;
    OnlineOrderProcessor onlineProcessor;
    onlineProcessor.processOrder(); // Calls the template method

    std::cout << "\n--- Processing In-Store Order ---" << std::endl;
    InStoreOrderProcessor inStoreProcessor;
    inStoreProcessor.processOrder(); // Calls the template method

    return 0;
}

Conclusion

The Template Method pattern, when implemented using abstract classes in C++ (or similar OOP languages), provides a powerful way to define a high-level algorithm while allowing subclasses to customize specific steps. It promotes code reuse, enforces consistency, and simplifies the extension of functionality, making it a valuable tool in object-oriented design.