Understanding the Template Method design pattern by eating at Chipotle
Template Method is a commonly used design pattern in programming and real life.
Before we dive into details of the pattern, let’s learn an important life lesson:
Chipotle 101: How to Order at Chipotle.
There are four steps involved:
- Choose a “vessel”: Burrito vs. Bowl vs. Tacos vs. Salad
- Add meat: Chicken vs Steak vs. Barbacoa vs. Carnitas vs. Vegetarian
- Add toppings: Tomato vs. Corn vs. Green Chili vs. Red Chili
- Add extras & drinks: Chips vs. Guacamole vs. Salsa vs. Beer vs. Soda
For example, my go-to order is Bowl + Steak + (Tomato + Corn) + Guacamole and my friend Amber’s go-to order is Burrito + Chicken + (Green Chili + Red Chili) + (Chips + Soda).
If we code our go-to orders in Ruby, they will look like:
When we order, we put everything we want into the vessel and return the stuffed vessel.
Unfortunately, Amber and I decided to go on a diet for a while. And we decided that when we ordered from Chipotle, we could only get tomato as a topping and no extras. So our choices are limited to:
- Vessel: Burrito vs. Bowl vs. Tacos vs. Salad
- Meat: Chicken vs. Steak vs. Barbacoa vs. Carnitas vs. Vegetarian
- Toppings: Tomato
- No extras & drinks
During the diet, our go-to orders have to be modified to:
- Sihui: Bowl + Steak + Tomato + No extras & drinks
- Amber: Burrito + Chicken + Tomato + No extras & drinks
Putting our orders down in Ruby, we have the following:
Since both our orders have the exact same toppings, extras, and order methods, it makes sense to pull them out as a parent class, DietOrder, and have DietOrderSihui and DietOrderAmber inherit from it.
Now our friend Ben wants to join our Chipotle Diet Club, and he likes Tacos with Carnitas. Then his order will be:
Ta-da, you just learned the Template Method design pattern! 🎉 🎉 🎉
Don’t believe me?
Take a look at the definition of the Template Method:
The Template Method pattern is a behavioral design pattern that
- defines the program skeleton of an algorithm in an operation,
- deferrs some steps to subclasses.
It lets one redefine certain steps of an algorithm without changing the algorithm’s structure.
Doesn’t this sound exactly like what we just did with our DietOrder and SihuiDietOrder/AmberDietOrder/BenDietOrder?
DietOrder defines the order skeleton: one can only get tomato as a topping and no extras & drinks, and one orders by picking a vessel and putting everything inside the chosen vessel.
SihuiDietOrder/AmberDietOrder/BenDietOrder redefine the vessel and meat depending on our personal preferences.
Let’s say a month passed by, and Amber and I followed our diet strictly. We decided to reward ourselves with cheat days!
On a cheat day, we have soda as our drinks. 🍺🍺🍺 And each of us can decide which day of the month will be our cheat day.
Since Ben is new to the club, he decides to stick to the diet strictly for a bit longer.
Let’s see how it looks in Ruby:
In DietOrder, we ask if today is a cheat day. If so, we can have Soda as an extra. Otherwise, there are no extras. And by default, today is not a cheat day.
Amber and I get to define our own cheat days:
Since Ben is sticking with the diet strictly, he doesn’t get a cheat day.
His class doesn’t need to change.
The is_cheat_day? method is a hook.
A hook provides a way for a subclass to implement an optional part of an algorithm.
If the subclass doesn’t care about the part, it can skip it and use the default implementation in the parent class.
In our case, is_cheat_day? is optional. SihuiDietOrder and AmberDietOrder implement it because we want to have a cheat day each month. But Ben does not want to have a cheat day. So BenDietOrder skips implementing is_cheat_day? and uses the default one from DietOrder, which always returns false.
There are two important object-oriented design principles used in the Template Method:
1. Encapsulate what varies.
In our case, the varying parts are vessel, meat, and is_cheat_day?. We encapsulate them in subclasses. For the parts that don’t vary, toppings and extras, we leave them in the parent class.
2. The Hollywood Principle: Don’t call us, we’ll call you.
Yes, The Hollywood Principle is a real thing.
In Hollywood, movie producers will tell actors: “Don’t call us, we’ll call you if we find a role that fits you.”
In programming, low-level components can participate in the computation, like AmberDietOrder defining its own is_cheat_day?, but the high-level components control when and how, like DietOrder calls is_cheat_day? within extras.
Takeaways:
One definition =>
The Template Method pattern is a behavioral design pattern that
- defines the program skeleton of an algorithm in an operation,
- deferrs some steps to subclasses.
It lets one redefine certain steps of an algorithm without changing the algorithm’s structure.
Two Design Principles =>
1. Encapsulate what varies.
2. The Hollywood Principle: Don’t call us, we’ll call you.
Or…
you can just take away a Chipotle order 🥙 🌮 🌯
Next time, we take our design & food adventure to 🍰🍰🍰