Design Pattern: Command and Concierge

Design Pattern: Command and Concierge

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:

  1. Get room service for dinner
  2. Get laundry service because we didn’t bring extra clothes
  3. 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:

  1. The chef in the kitchen starts cooking
  2. The cleaning department send a staff to our room to pick up our clothes
  3. The staff in the lobby grabs a travel guide and delivers it to our room

 

Let’s recap what just happened.

  1. We selected the services we wanted from the menu and submitted them to a concierge.
  2. The concierge wrote these service requests down as a list.
  3. After we hung up, instructed by the service menu, the concierge sent our requests to corresponding departments.
  4. Each department executed on the given request.

 

Let’s see the actions in Ruby.

1. We submitted these three requests to the concierge:

2. These requests went into a list the concierge keeps track of:

 

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.

 

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?

  1. If the hotel offers twenty services, instead of three, the method will be really long.
  2. Each time we want to offer new services or remove an existing service, we have to open the Concierge class and redefine the act_on_requests method.

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:

And the updated Concierge will look like:

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:

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:

 

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.

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

 

8 Comments Design Pattern: Command and Concierge

  1. RRRoy BBBean

    Great explanation. Thank you very much. Is Command Pattern an alternative to Strategy Pattern, or can they sometimes be used together?

  2. Sihui Huang

    Hi Roy,

    You are absolutely on the right track trying to understand the difference between Command Pattern and Strategy Pattern. At their cores, they are both examples of using the “Encapsulate what varies” design principle.

    Their differences are subtle and more about intention and semantic than structure.

    Strategy Pattern is about encapsulating a family of algorithms. Algorithms in the family are different ways of doing the “same thing”. For example, you can have a family of different output algorithms: print to console, print as HTML, and print as XML. In essence, they are all about the same thing: outputting data into a specific format. In this post, I also use different burgers as examples.

    When Strategy Pattern enables the program to do the same thing in different ways, Command Pattern allows the program to do different things. In the example used in our post, the different things are taking dinner order, laundry service, and delivering travel guide. In Command Pattern, the common interface for a command object is that it has to respond to execute (and undo if needed). Command Pattern also supports queueing request to execute later and logging requests.

    In a nutshell, Strategy Pattern is about different ways of doing the same thing while Command Pattern is more about doing different things. They both decouple an object making a request from the one that knows how to perform it.

    Let me know if you have any more questions. 🙂

  3. Patrik

    This article is amazing! I really dig how you made it easy (and fun!) to understand the Command Pattern.

  4. Trent

    Your series of articles on design patterns have some of the best explanations that I have ever come across!

Leave a Comment