第8章 命令を実行する: Command
Ruby
Published: 2019-06-25

「Rubyによるデザインパターン」の読書メモです。

コマンドをクラスにするパターン

class SlickButton
  attr_accessor :command

  def initialize(command)
    @command = command
  end

  def on_button_push
    @command.execute if @command
  end
end

class SaveCommand
  def execute
    p "#{self.class}##{__method__}"
  end
end

save_button = SlickButton.new(SaveCommand.new)
save_button.on_button_push

コマンドをコードブロックにするパターン

コマンドは何か特定のことを行うコードの単純なラッパーで、唯一の存在理由は、適切なタイミングでコードを実行することです。

class SlickButton
  attr_accessor :command

  def initialize(&block)
    @command = block
  end

  def on_button_push
    @command.call if @command
  end
end

new_button = SlickButton.new do
  p "#{self.class}"
end

new_button.on_button_push

記録 + 戻るの例

class Command
  attr_reader :description

  def initialize(description)
    @description = description
  end

  def execute
  end

  def unexecute
  end
end

class CreateFile < Command
  def initialize(path, contents)
    super("Create file: #{path}")
    @path = path
    @contents = contents
  end

  def execute
    p "#{self.class}##{__method__}"
  end

  def unexecute
    p "#{self.class}##{__method__}"
  end
end

class DeleteFile < Command
  def initialize(path)
    super("Delete file: #{path}")
    @path = path
  end

  def execute
    p "#{self.class}##{__method__}"
  end

  def unexecute
    p "#{self.class}##{__method__}"
  end
end

class CopyFile < Command
  def initialize(source, target)
    super("Copy file: #{source} to #{target}")
    @source = source
    @target = target
  end

  def execute
    p "#{self.class}##{__method__}"
  end

  def unexecute
    p "#{self.class}##{__method__}"
  end
end

class CompositeCommand < Command
  def initialize
    @commands = []
  end

  def add_command(cmd)
    @commands << cmd
  end

  def execute
    @commands.each {|cmd| cmd.execute}
  end

  def unexecute
    @commands.reverse.each {|cmd| cmd.unexecute}
  end

  def description
    description = ''
    @commands.each {|cmd| description += cmd.description + "\n"}
    description
  end
end

cmds = CompositeCommand.new
cmds.add_command(CreateFile.new('file1.txt', 'hello world'))
cmds.add_command(CopyFile.new('file1.txt', 'file2.txt'))
cmds.add_command(DeleteFile.new('file1.txt'))

puts cmds.description
p '---'
cmds.execute
p '---'
cmds.unexecute

注意

Commandパターンのポイントは何を行うかの決定と、それの実行とを分離することです。

まとめ

Commandパターンでは、ある特定の動作を実行するオブジェクトを構築します。「特定」という部分が重要です。