menu
Behavioralarrow_forward_ios
Command
taskCommand

Description

The Command Pattern is a behavioral design pattern where an object is used to encapsulate all the information needed to perform an action. This pattern allows you to decouple sender and receiver objects, providing a more flexible and extensible design.

Real World Example

We will pass all the logic related to our task to the TurnOnCommand class, which will be an implementation of the Command pattern.

class TurnOnCommand
  attr_reader :device

  def initialize(device)
    @device = device
  end

  def execute
    # Logic for turning on a specific device
    puts "Device turned on!" 
  end
end
When the Command is ready, we can deal with our transmitters. The first of them will be an application that will have two methods turn_on_light and turn_on_tv.

class Application
  def turn_on_light
    TurnOnCommand.new(:light).execute
  end

  def turn_on_tv
    TurnOnCommand.new(:tv).execute
  end
end
An Application with this structure basically only delegates tasks to our Command, allowing us to turn on both the TV and the light.

app = Application.new
app.turn_on_light # => puts "Device turned on!"
app.turn_on_tv    # => puts "Device turned on!"
For our next transmitter, we will build a slightly more advanced structure that will allow us to add new commands in a more dynamic way.

class RemoteController
  attr_reader :commands

  def initialize
    @commands = {}
  end

  def add_command(name, command)
    @commands[name] = command
  end

  def press_button(name)
    command = commands[name]
    command.execute if command
  end
end
Now we can add new commands dynamically.

# Create specific commands
turn_on_tv = TurnOnCommand.new(:tv)
turn_on_light = TurnOnCommand.new(:light)

# Create remote controller
remote_controller = RemoteController.new

# Add command to remote controller
remote_controller.add_command(:turn_on_light, turn_on_light)
remote_controller.add_command(:turn_on_tv, turn_on_tv)

# Use command
remote_controller.press_button(:turn_on_light) # => puts "Device turned on!"
Let’s imagine that we now want to be able to turn off the TV. Probably the first thought that comes to your mind is to create a new TurnOffCommand class. It could be implemented this way, but the Command pattern is often implemented with two methods, one is the one you created, which is responsible for executing the task, and the second one allows you to reverse the effect of the first method.
So let’s add an undo method.

class TurnOnCommand
  attr_reader :device

  def initialize(device)
    @device = device
  end

  def execute
    # Logic for turning on a specific device
    puts "Device turned on!"
  end

  def undo
    # Logic for turning off specific devices
    puts "Device turned off!"
  end
end
Of course, we also need to add a new method that allows us to use the undo method in the controller.

class RemoteController
  # ...

  def press_undo_button(name)
    command = commands[name]
    command.undo if command
  end
end
Now we can easily add a new function to our controller.

# Create specific commands
turn_on_tv = TurnOnCommand.new(:tv)

# Create remote controller
remote_controller = RemoteController.new

# Add command to remote controller
remote_controller.add_command(:turn_on_tv, turn_on_tv)

# Use commands
remote_controller.press_button(:turn_on_tv) # => puts "Device turned on!"
remote_controller.press_undo_button(:turn_on_tv) # => puts "Device turned off!"