Design Patterns in life and Ruby — gain an intuitive understanding of OO design patterns by linking them with real-life examples.
The Command Pattern’s definition is a stressful one to look at.
The Command Pattern:
- encapsulates a request as an object,
- thereby letting you parameterize other objects with different requests, queue or log requests, and support undoable operations.

Let’s forget about it for a second and take a trip to Hawaii.

And live in a luxury hotel.

We spent the day on the beach, scuba dived, and did some sightseeing. It's time to get back to the hotel to chill, eat, and plan for the next day.
After getting back to the hotel, we want to:
- Get room service for dinner
- Get laundry service because we didn’t bring extra clothes
- Get a travel guide for Kauai, the island we are going to tomorrow
We check out the hotel’s service menu and find three service items matching our needs.

We then call the front desk to place these three requests. A concierge picks up our call, write down our list of requests, and acts on each service request as instructed by the service menu.

Then each staff member executes according to each specific request:
- The chef in the kitchen starts cooking
- The cleaning department send a staff to our room to pick up our clothes
- The staff in the lobby grabs a travel guide and delivers it to our room
Let’s recap what just happened.
- We selected the services we wanted from the menu and submitted them to a concierge.
- The concierge wrote these service requests down as a list.
- After we hung up, instructed by the service menu, the concierge sent our requests to corresponding departments.
- Each department executed on the given request.
Let’s see the actions in Ruby.
1. We submitted these three requests to the concierge:
# a. We submitted our requests one by one to a concierge. we.submit_request_to(concierge, 'dinner_room_service') we.submit_request_to(concierge, 'laundry_service') we.submit_request_to(concierge, 'travel_guide')
2. These requests went into a list the concierge keeps track of:
class Concierge
attr_reader :request_list
def initialize
@request_list = []
end
end
class We
def submit_request_to(concierge, request)
concierge.request_list << request
end
end
Let’s see that in action (console):

As we can see, after we submitted three requests, these requests were in a request_list taking care by concierge.
3. Instructed by the service menu, the concierge sent our requests to corresponding departments.
class Concierge
attr_reader :request_list
def initialize
@request_list = []
end
def act_on_requests
@request_list.each do |request|
case request[:service]
when 'room_service'
Kitchen.execute(request[:data])
when 'laundry_service'
CleaningDepartment.execute(request[:data])
when 'trip_planning_service'
TripAdvisor.execute(request[:data])
else
raise 'Request Not Supported'
end
end
end
The code above should work fine.
Except for one thing….
It smells bad.

Specifically, the part where we have the switch cases:

Why does this part smell bad?
- If the hotel offers twenty services, instead of three, the method will be really long.
- Each time we want to offer new services or remove an existing service, we have to open the
Conciergeclass and redefine theact_on_requestsmethod.
The method knows too much and requires frequent changes. Having these two combinations together is almost always a bad thing.
Why?
A method that requires frequent changes is a method you need to update often and each time you update a piece of code is an opportunity to introduce new bugs to the system. When the method also knows a tone, a.k.a. it's long, the chances of messing it up when updating increases significantly. That's the reasoning behind a design principle we talked about earlier––encapsulate what varies.
Time to Refactor!
There must be a better way than this:

Take a closer look and think about it.

Let’s rephrase what the code is doing in English. We loop through the requests on the request list. For each request, according to its service type, we give the corresponding department related data and execute the request. Essentially, we loop through the requests and execute on each of them accordingly.
But what if each request actually knows how to execute itself?
Then the method can be as simple as:

Instead of letting the act_on_requests method decides how each request should be handled, we distribute that responsibility and knowledge back to each request and let it decide how to itself should be handled.
With that being said, our requests could look like this:
class RoomService
attr_reader :data, :kitchen
def initialize(data)
@data = data
@kitchen = Kitchen.new
end
def execute
kitchen.cook_for(data)
end
end
class LaundryService
attr_reader :data, :cleaning_dpt
def initialize(data)
@data = data
@cleaning_dpt = CleaningDepartment.new
end
def execute
cleaning_dpt.do_laundry_for(data)
end
end
class TripPlanningService
attr_reader :data, :tripAdvisor
def initialize(data)
@data = data
@tripAdvisor = TripAdvisor.new
end
def execute
tripAdvisor.plan_for(data)
end
end
And the updated Concierge will look like:
class Concierge
attr_reader :request_list
def initialize
@request_list = []
end
def act_on_requests
@request_list.each do |request|
request.execute
end
end
end
With the updated codes, here is how we, customers of the hotel, send requests to the concierge.

It is pretty easy to create another service.
For example, the hotel also allows us to reserve SPA:
class SpaReservationService
attr_reader :data, :spa_center
def initialize(data)
@data = data
@spa_center = SpaCenter.new
end
def execute
spa_center.reserve(data)
end
def undo
spa_center.cancel(data)
end
end
The service not only supports execute (making a spa reservation) but also undo (canceling the reservation).
Let’s say the hotel also provides another way to request services without having to call the concierge — a service requesting panel:

We can just press the button and the service with a default setting will be delivered to our room.
Here is the code for the ServicePanel:
class ServicePanel
attr_reader :button_a1, :button_a2, :button_b1, :button_b2
def initialize(a1_service, a2_service,
b1_service, b2_service)
@button_a1 = Button.new(a1_service)
@button_a2 = Button.new(a2_service)
@button_b1 = Button.new(b1_service)
@button_b2 = Button.new(b2_service)
end
end
class Button
attr_reader :service
def initialize(service)
@service = service
end
def on_button_click
service.execute
end
end
And here is how we can create a service panel:

??We are using the Command Pattern!??
Let’s revisit the definition of the Command Pattern:
The Command Pattern:
- encapsulates a request as an object,
- thereby letting you parameterize other objects with different requests, queue or log requests, and support undoable operations.
1. “encapsulates a request as an object,”
Each of the services class we created, RoomService, LaundryService, TripPlanningService, and SpaReservationService, is an example of encapsulating a request as an object.
Recap:

2. “thereby letting you parameterize other objects with different requests,”
The ServicePanel is an example of parameterizing an object with different requests.
Recap:

3. “queue or log requests,”
Our requests were queued while the concierge was taking them over the phone.
Recap:

4. and support undoable operations.
SpaReservationService supports undo.
Recap:

Advantages of The Command Pattern
The Command Pattern lets you:
- parameterize other objects with different requests,
- queue or log requests,
- and support undoable operations.
Yup, the advantages of the command pattern is encapsulated into its definition. ?
Takeaways
The Command Pattern:
- encapsulates a request as an object,
- thereby letting you parameterize other objects with different requests, queue or log requests, and support undoable operations.
Thanks for reading!
Don't forget to subscribe. 😀
Enjoyed the article?
My best content on Software Design, Rails, and Career in Dev. Delivered weekly.
I loved your explanation! Keep doing this (please!)