Rubyデザインパターン 5日目 : Command

Rubyデザインパターン学習のために、自分なりに読書の結果をまとめていくことに決めました。第5日目はCommandです。(http://www.amazon.co.jp/gp/product/4894712857/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4894712857&linkCode=as2&tag=morizyun00-22)

![スクリーンショット 2015-07-27 11.25.28.png](https://qiita-image-store.s3.amazonaws.com/0/80075/d68a4010-c177-4ef5-2f2e-899a76c76cb6.png)


# **<i class="fa fa-pencil"></i>　5日目 Command**

5日目はCommandパターンです。
本書ではGUIアプリケーションの構築が例に挙げられています。
ユーザーがスクリーン上の特定のボタンをクリックすると、`on_button_push`メソッドが呼ばれるクラスを構築するときの事例です。


# **<i class="fa fa-code"></i> Command サンプルコード**

```rb
class Button
  attr_accessor :command

  def initialize(command)
    @command = command
  end
  # ボタンの描画と管理のためのコード
  #
  #
  def on_button_push
    @command.execute if @command
  end
end

class SaveCommand # コマンドオブジェクト
  def execute
    # 現在の文書を保存
    #
    #
  end
end

save_button = Button.new(SaveCommand.new)
save_button.on_button_push # => 保存を実行
```

これがCommandパターンの一例です。
より一般的なButtonクラスを構築し、それに委譲する形でオブジェクトを渡します。
Buttonクラスは不変部分であり、そこに可変のコマンドオブジェクトを実装する形です。

**変わるところと変わらないところを分離するというパターン大原則**にならっています。


## **<i class="fa fa-cubes"></i> 引数にブロックを渡す**
**<i class="fa fa-diamond"></i> クラスではなくコードブロックを渡したいなら、こういった構成にするといいでしょう**
※ ただし、常にブロック渡しが有効とも限らないので、クラスベースの委譲方式も選択肢として持っておくべきでしょう！

```rb
class Button
  attr_accessor :command
  def initialize(&command)
    @command = command
  end
  #
  #
  def on_button_push #　ボタンが押されたら
    @command.call if @command
  end
end
```

## **<i class="fa fa-keyboard-o"></i> 実行コマンドを記録する**

```rb

require 'fileutils'

class Command # 基底クラス
  attr_accessor :description

  def initialize(description)
    @description = description
  end

  def execute # 空のexecuteメソッド
  end
end

class CreateFile < Command # Create 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
end

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

  def execute
    File.delete(@path)
  end
end

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

  def execute
    FileUtils.copy(@source, @target)
  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 description
    description = ''
    @commands.each { |cmd| description += cmd.description + "\n" }
    description
  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'))

cmds.execute
puts cmds.description
```

実行したコマンドを記録するためにComposite Commandクラスに`description`メソッドを定義してあります。

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

### **<i class="fa fa-check-circle-o"></i> Observerとの違い**
* **Commandパターン**
 * `add_command`でコマンドオブジェクトを`@commands`配列に登録し、それをイテレータを通して実行します。
* **Observerパターン**
 * `add_observer`でオブザーバオブジェクトを`@observers`配列に登録し、それをイテレータを通して実行します。そしてオブザーバオブジェクトは基底クラスの変更を見守っています。

唯一の違いは、**登録された側が基底クラス内のデータを<i class="fa fa-eye"></i>監視しているかどうか**、だけです。
Commandは渡した処理をただ単に実行するだけです。


### **<i class="fa fa-check-circle-o"></i> コマンドを使ったUndo**
 **単純なUndo機能を実装するのも簡単です**

```rb
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

file = CreateFile.new('hoge.rb', "何妙法蓮華")
file.execute

# file.unexecute これで簡単にUndoが実行できます

```

# **<i class="fa fa-star"></i>　まとめ**

実行したい機能を配列として持ち、それを一気に実行したりできるパターンです。
**機能を束ねる**といった色が強いでしょう。

パターンの構築方法としては Strategy + Composite + Observer = Command　のようにも取れるのでしょうか。
Commandは、パターンというよりは概念に近いかもしれません。


またRubyで容易に書くことができるパターンの一つなので、汎用性も高く、使い所は多そうですね。