Ruby Design Pattern: Command Method

Nakshtra
Nakshtra
Jan 22, 2018 · 5 min read

The command pattern is a behavior design pattern used to store the information necessary to call methods at a future time.

It encapsulate a request as an object, thereby letting you parametrize clients with different requests, queue or log requests, and support undoable operations.

Example: Lets take a quick example first:

The Command pattern allows requests to be encapsulated as objects, thereby allowing clients to be parametrized with different requests.
The “check” at a diner is an example of a Command pattern. The waiter or waitress takes an order or command from a customer and encapsulates that order by writing it on the check. The order is then queued for a short order cook.
Note that the pad of “checks” used by each waiter is not dependent on the menu, and therefore they can support commands to cook many different items.

Discussion

Command decouples the object that invokes the operation from the one that knows how to perform it. To achieve this separation, the designer creates an abstract base class that maps a receiver (an object) with an action (a pointer to a member function). The base class contains an execute() method that simply calls the action on the receiver.

All clients of Command objects treat each object as a “black box” by simply invoking the object’s virtual execute() method whenever the client requires the object's "service".

A Command class holds some subset of the following: an object, a method to be applied to the object, and the arguments to be passed when the method is applied. The Command’s “execute” method then causes the pieces to come together.

Sequences of Command objects can be assembled into composite (or macro) commands.


Example

Let’s consider a button implementation of some GUI framework, which has a method called upon button click.

class SlickButton
# Lots of button drawing and management code omitted...

def on_button_push
# Do something when the button is pushed
end
end

So, we could extend the button class overriding the on_button_push method to perform certain actions whenever a user clicks it. For example, if the button’s purpose is saving a document, we could do something like this:

class SaveButton < SlickButton
def on_button_push
# Save the current document...
end
end

However, a complex GUI could have hundreds of buttons, which means that we would end up having several hundreds of subclasses of our button. There is an easier way. We can factor out the code that performs the action into its own object, which implements a simple interface. Then, we can refactor our button’s implementation to receive the command object as a parameter and calling it when it’s clicked.

class SaveCommand
def execute
# Save the current document...
end
end
class SlickButton
attr_accessor :command

def initialize(command)
@command = command
end

def on_button_push
@command.execute if @command
end
end
save_button = SlickButton.new(SaveCommand.new)

The Command pattern is pretty useful if we need to implement undo feature. All we need to do is implementing the unexecute method in our command object. For example, this is how we would implement the task of creating a file:

class CreateFile < Command
def initialize(path, contents)
super "Create file: #{path}"
@path = path
@contents = contents
end
def execute
f = File.open(@path, "w")
f.write(@contents)
f.close
end
def unexecute
File.delete(@path)
end
end

Another situation where the command pattern is really handy is in installation programs. Combining it with the composite pattern, we can store a list of tasks to be performed:

class CompositeCommand < Command
def initialize
@commands = []
end
def add_command(cmd)
@commands << cmd
end
def execute
@commands.each {|cmd| cmd.execute}
end
end
cmds = CompositeCommand.new
cmds.add_command(CreateFile.new('file1.txt', "hello world\n"))
cmds.add_command(CopyFile.new('file1.txt', 'file2.txt'))
cmds.add_command(DeleteFile.new('file1.txt’))

Uses:

  • GUI Elements:
    For many GUIs, you may have a generic button class that you want to use in many different situations. In each situation, the button may need to do different things. One approach would be to create a subclass for each button your interface requires, however this would lead to a excessive number of button classes. A better approach would be to create separate classes for the code executed when the button is clicked. This action class can then be passed to the button telling it what the button should do when clicked.
    separate the thing that changes, in this case the action, from the part that stays the same, the generic button object. One advantage of this approach is that since the action is passed to the button, it can be changed at runtime.
    The command is merely a set of actions wrapped in an object. With ruby, we can use Procs to do the same thing without the need to create a separate object. This is a good option when the action is simple and doesn’t require saving state information, otherwise, a command class is the better option.
  • Macro Recording:
    Many modern applications have the ability to undo actions, including word processors, spreadsheets and databases. This undo feature can be implemented by using the command design pattern by keeping track what code is executed.
    Another great example of macro recording using the command design pattern is the handling of migrations in Ruby on Rails.
    To undo actions we need to store some state information, so we must use a command class rather than a simple Proc.
  • Queuing Commands:
    Commands allow us to queue commands making applications more convenient for the user and for the system.
  • Installation Wizards:
    When installing applications, many will prompt the user with a number of installation options before starting the actual installations. The user’s choices determines which method calls are added to the installer’s to-do list. Without commands, the installation would have to stop periodically to query the user for their preferences.
  • Fixed Overhead:
    In some situations there is a fixed overhead to executing a certain type of command. Queuing up multiple commands and executing them together reduces the number of times we have to run the overhead code. Database operations are an example of this. If there isn’t a persistent database connection, we have to create one each time we run database operations. Since there is a cost to connecting to the database, a good approach may be to queue up the database operations and execute them in a batch. The same logic holds for web applications when you need to make API calls to external applications.

Nakshtra

Written by

Nakshtra

If there is one substance by which everything is held together, it is love.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade