Design patterns in life and Ruby - gain an intuitive understanding of OO design patterns by linking them with real-life examples.
Do you know how to order a burger? If so, I have a good news for you: you already know how to use the Strategy Pattern!
How so, you might ask? Well, let’s take a look at the definition of Strategy Pattern.
The Strategy Pattern:
- defines a family of algorithms,
- encapsulates each algorithm, and
- makes the algorithms interchangeable within that family.
Strategy lets the algorithm vary independently from clients that use it.

You might feel as confused as Nick Young
How does this have anything to do with burgers? Let’s think about burgers for a second.
There are a lot of varieties of burgers: veggie burger, cheeseburger, grilled chicken burger, double cheeseburger…
All of them have the same form: top bun + stuffing + bottom bun.
It’s the different stuffing in the middle makes burgers different from each other.
A cheeseburger has cheese and a beef patty in the middle as stuffing, where a grilled chicken burger has grilled chicken breast as stuffing.
Let’s revisit the definition in light of burgers:
The Strategy Pattern: defines a family of algorithms, encapsulates each algorithm, and makes the algorithms interchangeable within that family.
What is the family of algorithms in terms of burgers? It’s the family of different stuffings:
- stuffing for a chicken burger = [grilled chicken breast]
- stuffing for a cheeseburger = [cheese + beef patty]
- stuffing for a double cheeseburger = [cheese + beef patty + cheese + beef patty]
They are encapsulated and are interchangeable with each other. Swap the stuffing of chicken burger with the stuffing of cheeseburger, you will get a cheeseburger!
Strategy lets the algorithm vary independently from clients that use it.
You can order whatever burger you like. But for a cook, making a burger follows the same general procedure: prepare the bun, cook the stuffing, and put the stuffing in between the top and bottom bun pieces.
A burger is a real-life example of using Strategy Pattern.
Let’s take a look at the code.
# Concrete Strategy class GrilledChickenStuffing def cook stuffing = [] stuffing << 'sliced tomato' stuffing << 'lettuce' stuffing << 'grilled chicken breast' stuffing end end class BeefPattyStuffing def cook stuffing = [] stuffing << 'cheese' stuffing << 'grilled beef patty' stuffing end end # Context class Burger attr_reader :top_bun, :bottom_bun attr_accessor :stuffing def initialize(stuffing) @top_bun = TOP_BUN @stuffing = stuffing @bottom_bun = BOTTOM_BUN end def cook burger = [] burger << top_bun.cook burger << stuffing.cook burger << bottom_bun.cook burger end end chicken_burger = Burger.new(GrilledChickenStuffing.new) cheese_burger = Burger.new(BeefPattyStuffing.new) # change the chicken_burger to a cheese burger chicken_burger.stuffing = BeefPattyStuffing.new
class GrilledChicken def cook stuffing = [] stuffing << 'sliced tomato' stuffing << 'lettuce' stuffing << 'grilled chicken' stuffing end end class BeefPatty def cook stuffing = [] stuffing << 'cheese' stuffing << 'grilled beef patty' stuffing end end
There are three participants in the Strategy Pattern.
Strategy — declares an interface common to all supported algorithms. Context uses this interface to call the algorithm defined by a ConcreteStrategy.
ConcreteStrategy — implements the algorithm using the StrategyInterface.
Context — is configured with a ConcreteStrategy Object; maintains a reference to a Strategy object; may define an interface that lets Strategy access its data.
GrilledChickenStuffing
and BeefPattyStuffing
are our concrete strategies — each of them defines how its stuffing should be cooked. The Burger
class is our context — it’s configured with a concrete strategy and it uses the concrete strategy later when a burger needs to be cooked.
In our burger example, we don’t have a specific class that declares the interface concrete strategies should implement. That’s because we don’t need to, thanks to Ruby’s duck typing: if it walks like a duck and talks like a duck, it is a duck. If it can be cooked as a burger stuffing, it is a burger stuffing.
Strategy declares the interface for which a concrete strategy should implement and for which a context can use. As you can see in the above code, both GrilledChickenStuffing
and BeefPattyStuffing
implement the cook
method which is the method a context, meaning a user of a concrete strategy, expects the concrete strategy to provide.
The key idea of the Strategy Pattern.
Strategy Pattern is about pulling the varying algorithm out into a separate object. These objects become a family of algorithms the context can choose from. Each of these of objects, aka the strategies, does the same job and supports the same interface.
In our burger example, we have different stuffing strategies for a burger. And each of the concrete burger stuffing strategies supports the same interface by implementing the cook
method.
It’s all about using composition (the user of the strategy has a strategy) and delegation (the user of the strategy delegate the job out). In our example, a burger has a stuffing, and it delegates the job of cooking the stuffing out.
Advantages of the Strategy Pattern
- It achieves better separation of concerns by pulling out a set of strategies from a class and relives the Burger class of any responsibility for or knowledge of the stuffing.
- It makes it easy to switch strategies at runtime because the pattern is based on composition and delegation rather than on inheritance.
Things to watch out for while considering using the Strategy Pattern
- Data passing between context and strategy: if the implementation of a concrete strategy requires data from the context, you can either pass the data as parameters to a concrete strategy or pass the context itself into a concrete strategy so the concrete strategy can have access to the data through the context. Whichever way you choose, watch out for having the context and the concrete strategy tangled up too much.
- Double check if you actually need the Strategy Pattern, the Template Method, or the Decorator Pattern.
Design Principles used in the Strategy Pattern
- Encapsulate what varies
- Favor composition over inheritance
- Program to interfaces, not implementation
Now you have learned about the Strategy Pattern.
Here comes an important question: what’s your favorite burger strategy? ?
Thanks for reading. =)
Don't forget to subscribe!
Next time we will look at Template Method and …
Enjoyed the article?
My best content on Software Design, Rails, and Career in Dev. Delivered weekly.
That’s nick young, not nick cannon.
Oops, thanks!
Nice explanation. Keep them coming.
Thanks, Deepak!
Will do 🙂
These are great! A really effective way of translating the bewildering pattern descriptions into simple visuals.
I’m glad you like them, Dan! 🙂