The Template Method pattern is a behavioural design pattern that defines the skeleton of an algorithm in a superclass but lets subclasses override specific steps of the algorithm without changing its structure. This pattern promotes code reuse and allows for variations in behaviour among subclasses.
Here's how the Template Method pattern works:
Abstract Template: Define an abstract class that contains a template method defining the steps of an algorithm. The template method typically calls one or more abstract methods, which are then implemented by concrete subclasses to provide specific behaviour.
Concrete Subclasses: Create concrete subclasses that inherit from the abstract class. These subclasses provide implementations for the abstract methods defined in the superclass, thus customising the behaviour of the algorithm.
Template Method Invocation: Clients interact with the template method defined in the abstract class. They can use the template method without being aware of the specific implementations of the abstract methods provided by concrete subclasses.
Example
Let's create a Swift example to demonstrate the Template Method pattern. We'll create a simple notification system where we have a base class Notification with a template method sendNotification(). Subclasses EmailNotification and SMSNotification will provide specific implementations for formatting messages and selecting recipients.
// Abstract class defining the template method
class Notification {
func sendNotification() {
let message = formatMessage()
let recipients = selectRecipients()
sendMessage(message: message, recipients: recipients)
}
// Abstract methods to be implemented by subclasses
func formatMessage() -> String {
fatalError("Must override formatMessage()")
}
func selectRecipients() -> [String] {
fatalError("Must override selectRecipients()")
}
// Common method used by the template method
func sendMessage(message: String, recipients: [String]) {
// Code to send the message to recipients
print("Message: \(message)")
print("Recipients: \(recipients.joined(separator: ", "))")
print("Notification sent successfully.")
}
}
// Concrete subclass implementing email notifications
class EmailNotification: Notification {
override func formatMessage() -> String {
return "Email notification message"
}
override func selectRecipients() -> [String] {
return ["email@example.com"]
}
}
// Concrete subclass implementing SMS notifications
class SMSNotification: Notification {
override func formatMessage() -> String {
return "SMS notification message"
}
override func selectRecipients() -> [String] {
return ["+1234567890"]
}
}
// Client code
let emailNotification = EmailNotification()
emailNotification.sendNotification()
let smsNotification = SMSNotification()
smsNotification.sendNotification()
In this Swift example:
Notification is a base class defining the template method sendNotification(), along with abstract methods formatMessage() and selectRecipients().
EmailNotification and SMSNotification are concrete subclasses that override the abstract methods to provide specific implementations for formatting messages and selecting recipients.
Clients interact with the sendNotification() method of concrete subclasses without being aware of the specific implementations provided by subclasses.
This demonstrates how the Template Method pattern can be implemented in Swift to define a common algorithm structure with variations in behaviour provided by subclasses.
Usage
The Template Method pattern is used in various scenarios where you want to define the overall structure of an algorithm but allow subclasses to customise certain steps.
Here are some common situations where the Template Method pattern is beneficial:
Framework Development: When developing frameworks or libraries, the Template Method pattern is commonly used to define the overall structure of the framework while allowing users to customise certain behaviours by subclassing and implementing template methods.
Algorithm Design: In algorithm design, you may have a general algorithm with some steps that are common across different variations of the algorithm, but other steps that need to be customised. The Template Method pattern helps in defining the common structure of the algorithm while allowing for customisation.
Code Reuse: If you have several classes that share a similar algorithm but differ in certain implementation details, you can use the Template Method pattern to avoid code duplication by extracting the common parts into a superclass.
Life Cycle Methods: In object-oriented programming, objects often have life cycles with common initialisation, processing, and cleanup steps. The Template Method pattern can be used to define a standard life cycle with hooks for subclasses to customise behaviour at various stages.
Concurrency Control: In concurrent programming, you may have a common pattern for executing tasks asynchronously or in parallel, but with variations in how tasks are created, scheduled, or executed. The Template Method pattern can be used to define the common concurrency control logic while allowing subclasses to customise aspects such as task creation or scheduling.
UI Design: When designing user interfaces, you may have common patterns for laying out components, handling events, or performing validations. The Template Method pattern can help in defining reusable UI components with customisable behaviour.
Testing Frameworks: Testing frameworks often have common setup, execution, and teardown phases for running tests. The Template Method pattern can be used to define the common test execution logic while allowing for customisation of test setup or teardown.
Database Access: When implementing data access layers, you may have common patterns for opening connections, executing queries, and handling transactions. The Template Method pattern can help in defining reusable data access methods with customisable query execution or transaction handling.
Summary
In summary, the Template Method pattern is a powerful design pattern that promotes code reuse and flexibility by defining the overall structure of an algorithm while allowing subclasses to customize specific steps. It's widely used in various domains such as framework development, algorithm design, concurrency control, UI design, testing frameworks, and database access layers.
Top comments (0)