This pattern a simple but deceptively powerful technique that decouples events — such as user interface interactions — from the concrete actions those events should trigger. The command in the command pattern couldn’t be simpler: it is the combination of a thing and an action. It’s an object and a method to be invoked on that object. That’s it! By itself, a command doesn’t count as a Pattern™. It’s just another object. What makes the pattern is the “how” and the “why” it gets used. “How” command objects get used is via a set of similar objects. These invoker objects are usually (but are not limited to) UI elements—think buttons or menu items. And they need to be able to tell another object to do something. The pattern arises from a natural desire for consistency. You don’t want one button to call a function while another invokes a command via a call() method while a third uses a do() method. That’s silly. The common sense desire for all buttons to invoke their commands in the same way is what gives rise to the pattern. Consistency is not the sole benefit of the command pattern. Once you have a command object that can tell a series of objects to do something, it is a short distance to being able to tell those same objects to undo those actions. It also gets easier to combine actions into macro commands.
# Pattern name: receiver
class Robot
  def initialize(x = 0, y = 0)
    @x, @y, = x, y
  end
  def location
    "x[#{@x}],y[#{@y}]"
  end
  def move_right
    @x += 1
  end
  def move_left
    @x -= 1
  end
  def move_up
    @y += 1
  end
  def move_down
    @y -= 1
  end
end
# Pattern name: invoker
class Button
  def initialize(name, command)
    @name, @command = name, command
  end
  def press
    @command.()
  end
end
def start
  puts '*** Robot Stuffs ***'
  r = Robot.new
  puts "Robot starts at: #{r.location}"
  button_right = Button.new('Right', r.method(:move_right))
  button_left  = Button.new('Left', r.method(:move_left))
  button_up    = Button.new('Up', r.method(:move_up))
  button_down  = Button.new('Down', r.method(:move_down))
  button_right.press
  puts "Robot current location: #{r.location}"
  button_left.press
  puts "Robot current location: #{r.location}"
  button_up.press
  puts "Robot current location: #{r.location}"
  button_down.press
  puts "Robot current location: #{r.location}"
  puts '---'
  puts "Robot ends at: #{r.location}"
end
The fun stuff with the command pattern starts with undoing commands. After pressing the “Up” button, we ought to be able to hit the “Undo” button to return things to their previous state.
# Pattern name: command
module Command
  def call
  end
  def undo
  end
end
class MoveRight
  include Command
  def initialize(receiver)
    @receiver = receiver
  end
  def call
    @receiver.move_right
  end
  def undo
    @receiver.move_left
  end
end
class MoveLeft
  include Command
  def initialize(receiver)
    @receiver = receiver
  end
  def call
    @receiver.move_left
  end
  def undo
    @receiver.move_right
  end
end
class MoveUp
  include Command
  def initialize(receiver)
    @receiver = receiver
  end
  def call
    @receiver.move_up
  end
  def undo
    @receiver.move_down
  end
end
class MoveDown
  include Command
  def initialize(receiver)
    @receiver = receiver
  end
  def call
    @receiver.move_down
  end
  def undo
    @receiver.move_up
  end
end
class History
  class << self
    def stack
      @stack ||= []
    end
    def add(command)
      stack << command
    end
    def undo
      command = stack.pop
      command.undo
      puts "Undoing #{command.class} command"
    end
    def undo_all
      stack.each do |command|
        command.undo
        puts "Undoing #{command.class} command"
      end
    end
  end
end
# Pattern name: invoker
class Button
  def initialize(name, command)
    @name, @command = name, command
  end
  def press
    History.add @command
    @command.call
  end
end
def start
  puts '*** Robot Stuffs ***'
  r = Robot.new
  puts "Robot starts at: #{r.location}"
  move_right = MoveRight.new r
  move_left  = MoveLeft.new r
  move_up    = MoveUp.new r
  move_down  = MoveDown.new r
  button_right = Button.new('Right', move_right)
  button_left  = Button.new('Left', move_left)
  button_up    = Button.new('Up', move_up)
  button_down  = Button.new('Down', move_down)
  button_right.press
  puts "Robot current location: #{r.location}"
  button_right.press
  puts "Robot current location: #{r.location}"
  button_right.press
  puts "Robot current location: #{r.location}"
  button_left.press
  puts "Robot current location: #{r.location}"
  button_up.press
  puts "Robot current location: #{r.location}"
  button_down.press
  puts "Robot current location: #{r.location}"
  History.undo
  History.undo_all
  puts '---'
  puts "Robot ends at: #{r.location}"
end