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.
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!