Design Pattern: Template Method and Chipotle

Design Pattern: Template Method and Chipotle

Design patterns in life and Ruby - gain an intuitive understanding of OO design patterns by linking them with real-life examples.

 

Template Method is the most 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 in Chipotle.

There are four steps involved:

  1. Choose a “vessel”: Burrito vs. Bowl vs. Tacos vs. Salad
  2. Add meat: Chicken vs Steak vs. Barbacoa vs. Carnitas vs. Vegetarian
  3. Add toppings: Tomato vs. Corn vs. Green Chili vs. Red Chili
  4. 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:

class GoToOrderSihui
  def vessel
    Bowl
  end
  
  def meat
    Steak
  end
  
  def toppings
    Tomato + Corn
  end
  
  def extras
    Guacamole
  end
  
  def order
    meal = vessel
    meal << meat
    meal << toppings
    meal << extras
    meal
  end
end

class GoToOrderAmber
  def vessel
    Burrito
  end
  
  def meat
    Chicken
  end
  
  def toppings
    Green Chili + Red Chili
  end
  
  def extras
    Chips + Soda
  end
  
  def order
    meal = vessel
    meal << meat
    meal << toppings
    meal << extras
    meal
  end
end

 

When we order, we put everything we want into the vessel and return the stuffed vessel.

Unfortunately, Amber and I decide to be on a diet for a while. And we decide that when we order from Chipotle, we can only get tomato as a topping and no extras. So our choices are limited to:

  1. Vessel: Burrito vs. Bowl vs. Tacos vs. Salad
  2. Meat: Chicken vs. Steak vs. Barbacoa vs. Carnitas vs. Vegetarian
  3. Toppings: Tomato
  4. 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:

class DietOrderSihui
  def vessel
    Bowl
  end
  
  def meat
    Steak
  end
  
  def toppings
    Tomato
  end
  
  def extras
    nil
  end
  
  def order
    meal = vessel
    meal << meat
    meal << toppings
    meal << extras
    meal
  end
end

class DietOrderAmber
  def vessel
    Burrito
  end
  
  def meat
    Chicken
  end
  
  def toppings
    Tomato
  end
  
  def extras
    nil
  end
  
  def order
    meal = vessel
    meal << meat
    meal << toppings
    meal << extras
    meal
  end
end

 

Noticing both our orders have the exact same toppingsextras, and order methods, it makes sense to pull them out as a parent class, DietOrder, and have DietOrderSihui and DietOrderAmber inherit from it.

class DietOrder
  def vessel
    raise 'What is your choice?'
  end
  
  def meat
    raise 'What is your choice?'
  end
  
  def toppings
    Tomato
  end
  
  def extras
    nil
  end
  
  def order
    meal = vessel
    meal << meat
    meal << toppings
    meal << extras
    meal
  end
end

class DietOrderSihui < DietOrder
  def vessel
    Bowl
  end
  
  def meat
    Steak
  end
end

class DietOrderAmber < DietOrder
  def vessel
    Burrito
  end
  
  def meat
    Chicken
  end
end

 

Now our friend Ben wants to join our Chipotle Diet Club, and he likes Tacos with Carnitas. Then his order will be:

class BenDietOrder < DietOrder
  def vessel
    Tacos
  end
  
  def meat
    Carnitas
  end
end

 

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,

- deferring 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 to be our cheat days.

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:

class DietOrder
  def vessel
    raise 'What is your choice?'
  end
  
  def meat
    raise 'What is your choice?'
  end
  
  def toppings
    Tomato
  end
  
  def extras
    is_cheat_day? ? Soda : nil
  end
  
  def is_cheat_day?
    false
  end
  
  def order
    meal = vessel
    meal << meat
    meal << toppings
    meal << extras
    meal
  end
end

 

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:

require 'date'

class DietOrderSihui < DietOrder
  def vessel
    Bowl
  end
  
  def meat
    Steak
  end
  
  def is_cheat_day?
    Date.today.day == 10
  end
end

class DietOrderAmber < DietOrder
  def vessel
    Burrito
  end
  
  def meat
    Chicken
  end
  
  def is_cheat_day?
    Date.today.day == 25
  end
end

Since Ben is sticking with the diet strictly, he doesn’t get a cheat day.

His class doesn’t need to change.

class BenDietOrder < DietOrder
  def vessel
    Tacos
  end
  
  def meat
    Carnitas
  end
end

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.

 

Two Object-oriented Design Principles

The Template Method uses two important object-oriented design principles:

1. Encapsulate what varies.

In our case, the varying parts are vesselmeat, 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,

- deferring 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 The Cheesecake Factory!

 

Subscribe so you won't miss it!

Enjoyed the article?

My best content on Software Design, Rails, and Career in Dev. Delivered weekly.

Unsubscribe at anytime. I'll never spam you. Powered by ConvertKit

1 Comment Design Pattern: Template Method and Chipotle

  1. Munir

    Thanks for the great post. Been struggling to grasp all design patterns. I think I got the right place now. You know how to explain complex ideas in easy ways. Very clear and easy to understand and real life example. Feel happy to see this kind of blog exists !

Leave A Comment

Your email address will not be published. Required fields are marked *