On design patterns
I wanted to take some time to write about a subject that, in my view, we in ruby land have an easy time ignoring: design patterns. Ruby’s expressiveness and readability make it easy to forget that the maintainability of code largely depends on its design — that step we should be taking before mashing our hands on the keyboard (a step I’ve been hugely guilty of skipping earlier in my career).
Design patterns are an incredibly important tool for any developer to have available to them, and a familiarity with them will lead to better code by forcing a consideration of SOLID design. It’s important to remember that there is no magic bullet: design patterns aren’t always the answer and when to use which pattern is largely subjective, but it certainly helps to be able to consider these options when writing software.
I have found that when I start with a design pattern I tend to write more testable, readable code. I’ve also found that just by knowing about some of these patterns I’m able to approach the design of my code with much more confidence. In the spirit of sharing that with others I’d like to start by talking about one of the simpler patterns: the Template Method.
What is the Template Method pattern?
The Template Method allows you to define the “skeleton” of an algorithm while leaving concrete implementation up to inheriting classes. The template (in this case, the parent class) serves as an outline of the steps needed to complete the task, itself implementing any steps that don’t vary and defining an interface for the steps that do. It’s concrete-class children then go on to implement those missing steps.
It’s a really simple pattern that merely takes advantage of the fact that child classes get their parent’s methods for free, but can also override them at will.
When it might be a good idea
This pattern is useful anytime you have an algorithm that has one or more steps that need to vary, or when you have a couple algorithms that are very nearly the same but differ by just a step or two.
Rather than having multiple classes that implement a nearly identical set of instructions, or trying to mash up methods that are really different responsibilities into one class, you can cleanly seperate concerns into their own implementations.
Keep YAGNI in mind, though. Don’t use this pattern just because you think part of an algorithm might one day need to change (that day never comes). Just start with a concrete class and, if a new requirement ever actually arrives, only then consider breaking out the implementation into abstract and concrete classes.
Walking through an example
Let’s say we’ve been tasked with writing some code that will generate a financial report for our company and then email it to a few stakeholders in HTML format.
It’s implementation might look something like this:
To keep it simple, we’ve omitted some details but the point is that a client of this class simply calls
generate_report! and it takes a series of steps to handle generating the report.
And this is fine and everything works for a while, but then one day a new requirement comes along. It turns out not all our stakeholders read HTML formatted emails, instead preferring plain text. So we need to modify our code somehow so that it can support either format. We could add additional methods to the
Report class and an options flag but our class would start to accumulate greater and greater complexity as we add more formats.
Enter the Template Method pattern. We have an algorithm (the
generate_report! method in our
Report class) and most of the steps are consistent, however we need to vary one of them (
format_report ). In order to accomplish this we’ll turn our
Report class into an abstract parent class by raising an error if it’s version of the method is called. We’ll also change the class’s name to make it more clear what we’re doing.
We’ll then create a couple concrete classes that implement the
Now any client of this report can call either
HTMLReport depending on which version they want. New formats can be added by creating new concrete classes, rather than mucking around in existing ones, a great example of the Open/Closed Principle in action. By designing our code this way it will be easier and safer to change in the future. You can’t break non-dependent code that you don’t have to change.
Overall, the structure of our code now looks like this:
The Template Method helps us seperate code that changes from code that doesn’t. It helps us keep our concerns cleanly separated, and makes future change easier and safer.
On the other hand, the inheritance on which this pattern relies can be a sledge hammer. Be cautious about exactly when you use this pattern, especially in cases where composition is available instead. Additionally, if there are multiple parts of the algorithm that need to change the number of concrete subclasses can quickly get out of hand.
In the example above, if we had to vary both our formatting and our delivery method (for instance, being able to upload the report to an FTP instead of emailing it) and we had more formatting options, we’d end up with potentially many subclasses. Scenarios like this might be better handled by another design pattern, such as Strategy. We’ll cover that one later.
The Template Method is a powerful tool for keeping our code SOLID and DRY. It’s great when you have an algorithm that needs to change, but only in part, in different contexts. As with all things it can be abused, so be diligent in its application.
Now, go add this tool to your belt!