The Template Method pattern defines the skeleton of an algorithm or operation in high-level steps. Users or subclasses can override or implement the behavior of specific steps within the algorithm, but are not able to modify the general algorithm flow itself.
This design pattern is categorized as a “behavioral class” pattern in the Design Patterns book by Gamma et al., who summarize the pattern in this way:
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.
Table of Contents:
Context
The intent of this pattern is to define and enforce the overall structure of an operation or algorithm, while allowing users or subclasses to refine, redefine, or implement specific steps. Users are not able to modify the overall algorithm or its structure (for that, you would use the Strategy pattern).
This pattern makes use of Inversion of Control. The library, framework, or base class controls the operation, but it invokes functions in user code or subclasses which implement individual steps.
Problem
The Template Method pattern can be used to address two common situations that arise in software development.
As you develop a system, you will often naturally develop functions or classes that are largely duplicated in their functionality, but varying in small individual implementation details. This often happens as system grows because a specific operation or algorithm needs to be extended or customized for different specific use cases. In other cases, it happens because existing library or SDK code needs to be adjusted to be better suit your system’s needs. In any case, wouldn’t it be preferable for the common code to be kept in a single place, eliminating the duplicated behavior across all of the implementations, while still preserving the ability to customize individual operational steps for specific use cases?
That is the original problem that the Template Method pattern was targeted at.
Embedded software developers deal with the same problem described above, but also have another common challenge. Every embedded device is essentially unique in its combination of hardware and software dependencies: different processors, vendor SDKs, OSes, libraries, frameworks, hardware components, and PCB layouts contribute to completely unique products. A large amount of embedded code could be portable and reusable, but implementations are often tightly coupled to a unique platform due to the need to account for the unique hardware and software makeup of that platform. Wouldn’t it be better to structure embedded software in such a way that users could implement or override specific steps (such sending and receiving data over a SPI or I2C bus) to support their unique hardware/software dependencies, allowing the implementation to remain independent of a specific platform?
Forces
In these scenarios, you are trying to balance control with customization while minimizing duplication. You need to define an overall operation, but users or subclasses need the ability to vary individual steps in the operation. The naive approach is to customize the operation by making a copy of it and varying the steps as necessary. However, this leads to duplication of the common steps of the algorithm, and if a change has to be made it must be propagated to all of the implementations. There is also no enforcement of the operation across the duplicate implementations: they can deviate in behavior over time.
With the Template Method pattern, you can enforce control of the operational flow while giving clients the ability to customize individual steps of the operation. Common code can be kept in a single location, eliminating duplication. Thanks to the eliminated duplication and customizability, you increase the possibility of reusing the code for new purposes.
Reuse is also improved by the reduction of coupling that this pattern enables, which is specifically beneficial to the embedded scenario above. Embedded software is often tightly coupled to the underlying hardware/software platform. However, we can leverage the customization of individual steps to allow users to implement functionality specifically for their system outside of the primary implementation. This also improves the portability of the software.
Solution
In a base class, library, or framework, define a Template Method which contains the invariant code (the common code) for the overall algorithm or operation. The operation is broken down into a series of steps. The distinct steps, especially those that can or must be customized, are factored into individual functions. Individual steps may be controlled completely by the implementation, or they may be customizable by the user.
The customizable steps (sometimes called “helper methods” or “customization points”) of a Template Method can be further categorized in the following way:
- Abstract steps, which do not supply a default implementation and must be provided by every subclass or application
- Optional steps, which supply a default implementation but can be overridden if desired
- Hooks, which are optional steps with an empty body that provide additional extension points for an algorithm
- Hooks are often placed before and after crucial steps of an operation to provide additional extension points
In order to use the Template Method, subclasses and application code will supply any abstract steps and optionally override any optional steps. This mechanism ensures that the overall operation or algorithm flow is enforced while granting users the ability to override specific details.
The mechanisms used to implement this pattern depend on the chosen language. Common choices include:
- Function pointers
- Weakly linked functions
- Inheritance
- C++ Templates
Consequences
The Template Method pattern eliminates duplication across implementations by keeping the common operational code in a single location. The pattern also gives the base implementation control over the general algorithm while giving clients the ability to customize specific implementation steps. This improves reusability of the code, as users can modify steps to suit the situation. The pattern can also be used to decouple the operation from a specific hardware or software platform, since implementation details can be supplied through abstract steps that the user is required to implement.
Template Methods make use of Inversion of Control – the “lower-level” code (the library, framework, or base class) invokes “higher-level” code (an application or subclass). While useful, this can make the processing flow difficult to trace for those not familiar with the software, as some implementation details in the higher-level code will appear to be present but not used by the application. When debugging, this can lead to a “ping pong” or “yo yo” effect, where you are tracing back-and-forth between the code that defines the Template Method and the code that implements individual steps.
Users must be able to identify which operations they are required to implement (“abstract operations”) and which are optional. This is best addressed in the interface documentation. Users can also be aided by using a naming convention to help identify optional and required steps to implement. Failure to document these details reduces the likelihood that the code will be reused.
Gamma et al. point out (and we agree) that an important design goal when using Template Methods is to minimize the number of operations that a user must implement. The more operations that need to be overridden, the more tedious it is to use the code. Note that this is different from giving users the option to override specific steps that have a default implementation.
The indirection introduced by the use of this pattern will incur a slight performance penalty. In nearly all cases, this will have no impact on the system’s operation. However, if a Template Method is invoked frequently (i.e., in a hot loop), this pattern may introduce enough overhead that a direct function call would be preferred. The system should be measured to confirm the cause of the performance bottleneck.
Known Uses
- This pattern is fundamental and is often used by abstract class hierarchies, libraries, frameworks, and SDKs to provide customization of default behavior or to provide methods that must be implemented according to the specific use case.
- Template Methods can be used to decouple software from external dependencies, such as specific hardware components or operating systems. Users can supply platform-specific implementation details through template methods, allowing the original code to be decoupled from any particular platform.
- Template Methods are often used within generated code to allow users to customize behavior without needing to directly modify the generated code. This is often called the Generation Gap pattern.
- Template Method can be used to refactor classes or functions with a significant amount of code duplication, but variation in individual steps. Martin Fowler captures this process in the Form Template Method refactoring pattern.
- Identify the differences in the existing code.
- Separate the differences into new operations.
- Replace the differing code with a Template Method that calls the new operations.
Examples
- The Embedded Artistry Arduino Logging Library (GitHub) uses the Template Method pattern by provide customizable steps for the standard
log()function implementation. - Practical Decoupling Techniques Applied to a C-based Radio Driver shows a use of the Template Method pattern to decouple the driver from the underlying SPI communication bus implementation.
- The following pieces of software analyzed in the Designing Embedded Software for Change course make use of the Template Method pattern:
Variants
- The Generation Gap pattern is a specialization of the Template Method pattern to ensure users do not directly modify generated code.
- Factory Methods can be viewed as a specialization of the Template Method pattern.
Related Patterns
- The Strategy Pattern can be viewed a larger-scale extension of the ideas behind the Template Method: Strategy varies an algorithm in its entirety, where Template Method is used to vary individual steps of an algorithm while enforcing an overall algorithm structure.
- Others distinguish these two patterns by saying that Template Method uses inheritance while Strategy uses delegation (delegating the algorithm to another section of code). We find this particular distinction as artificially limiting, since the idea behind Template Method can be applied even without inheritance and can also be viewed as delegation.
- The Non-Virtual Interface (NVI) Pattern is a way to implement the Template Method pattern
- Factory Methods and Template Methods can often be found together, with a Factory Method being used as a step within a larger Template Method.
- Callback Functions operate similarly to the Template Method Pattern. For both patterns, tight coupling between modules can be handled externally by supplying an implementation for an optional customizable step. Callback operations are conceptually focused on customizing what happens when an operation is completed or event occurs, whereas the Template Method pattern is conceptually focused on customizing what happens during an operation. Given this, callbacks can be viewed as an application of Template Method.
- Martin Fowler describes a Form Template Method refactoring pattern. When you notice that two related methods perform similar steps in the same order, yet the steps are different, you can refactor to a Template Method pattern.
- Gamma et al. describe refactoring using Template Method as well.
References
-
Design Patterns by Gamma et al.
-
Making Embedded Systems: Design Patterns for Great Software, 2nd edition, by Elecia White
A related pattern is the template pattern. A template provides a skeleton of an algorithm but allows the steps to change (without changing the algorithm’s structure). Usually these aren’t function pointers; the steps are part of the organization of the algorithm. In our data-driven system, we could make a template that looked like the following:
[…]
Even though each of these functions has the same prototype, they aren’t interchangeable, unlike the strategy pattern. Instead, they provide an outline of how the system works. An instance of this template for the system described would have the ADC sampling the data, the data being amplified, and the DAC outputting the result. The instantiation of a template may override certain (or all) parts of the default implementation. For instance, if the default implementation sets up the order “ADC-process-DAC,” a test version of the class may override the sample function to read data from a file while leaving the other two functions in place.
[…]
Object-oriented software has the concept of inheritance, where an instance is-a concrete version of an object. Template patterns are like that (the template is a series of these steps). An alternative approach is composition, where the software has-a concrete version of an object. Strategy patterns are more like that (they offer a function to call). Composition is more flexible than inheritance because it is easier to switch what things have than what they are. On the other hand, building (composing) a system at runtime might not be a good use of limited resources. Balance the trade-offs for your system.
-
Design Patterns VS Design Principles: Template Method – Fluent C++
-
The Template Method – Modernes C++ by Rainer Grimm
-
Form Template Method – Refactoring Patterns
You have two methods in subclasses that perform similar steps in the same order, yet the steps are different.
Get the steps into methods with the same signature, so that the original methods become the same. Then you can pull them up.
« Back to Glossary Index
