Refactoring with Design Patterns - The Template Pattern
In our last article of the refactoring series we saw how design patterns can be used to make our Ruby code beautiful and clean. Design patterns are a powerful tool for any developer and a familiarity with them will lead to better code by forcing a consideration of SOLID principles.
Now let’s talk about other pattern that when properly used can be very helpful: The Template Method.
The Template Method
The Template Method is described as “a behavioral design pattern that lets you define the skeleton of an algorithm and allow subclasses to redefine certain steps of the algorithm without changing its structure.”
The goal is to separate code that changes from code that doesn’t change, keeping the concerns isolated on specialized classes. Those subclasses will then implement all the specific steps.
Check the image below that is illustrating the Template method structure:
Show me The Code
Lets say that we are still building the employees application from our last article and now we need to send an email to the managers with a report containing the amount of hours worked for each employee.
The implementation of the Report
class is quite simple:
class Report
def generate_report!
get_employees_worked_time
format_report
send_to_stakeholders
end
def get_employees_worked_time
# Retrieve this info from the database
end
def format_report
# Generate the HTML with the Report design
end
def send_to_stakeholders
# Call send email service
end
end
That code works perfectly fine. But what if now we also need to generate the same report in text format? The only part that will vary is exactly the format report step, so that is a scenario when applying the Template Method is a good choice.
To apply this pattern we will transform the Report
class into an abstract class that can be inherited from several concrete classes.
Back to our code, the only change necessary is to leave the implementation of the format_report method for the children class:
class ReportTemplate
def generate_report!
get_employees_worked_time
format_report
send_to_stakeholders
end
def get_employees_worked_time
# Retrieve this info from the database
end
def format_report
raise NotImplementedError
end
def send_to_stakeholders
# Call send email routine
end
end
And for each variation of an report we need to create a concrete subclass:
class HTMLReport < ReportTemplate
def format_report
# implement the report in HTML format
end
end
class TextReport < ReportTemplate
def format_report
# implement the report in Text format
end
end
Looks good, right? If we ever need to create a new format, we just need to create a new concrete class, making this a perfect example of the Open/Closed Principle . And by designing our code this way it will be easier and safer to change anything in the future.
Conclusion
The Template method is a powerful tool that every developer needs to have on hand. If you need to vary just a few methods or make them optional this pattern is a perfect solution. The template class should implement the skeleton, while the subclasses should implement the details in the way that it needs.
I hope that this was useful for you. We will keep talking about principles and patterns here in our blog, so stay tuned!