酒と泪とRubyとRailsと

Ruby on Rails と Objective-C は酒の肴です!

コマンド Ruby 2.0.0 デザインパターン速攻習得[Command][Design Pattern]

GoFのデザインパターン(Design Pattern)のコマンド(Command )のRubyコードを使った紹介記事です。

コマンドデザインパターンは、あるオブジェクトに対してコマンドを送ることでそのオブジェクトのメソッドを呼び出すことです。

例えば、ファイルシステムの実装は知らなくてもユーザーはファイルの追加、削除といったコマンドを実行できます。これもコマンドパターンの一つです。


コマンドの構成要素

コマンドの構成要素は、シンプルに2つです。

Command(コマンド):コマンドのインターフェイス
ConcreteCommand(具体コマンド):Commandの具体的な処理

コマンドのメリット

コマンドの変更・追加・削除に対して柔軟になる

ソースコード

コマンドデザインパターンを説明するために、ファイルの作成・削除・コピーができるモデルを考えます。

Commandクラス:すべてのCommandのインターフェイス
CreateFileクラス(ConcreteCommand):ファイルを作成する
DeleteFileクラス(ConcreteCommand):ファイルを削除する
CopyFileクラス(ConcreteCommand):ファイルをコピーする
CompositeCommand(ConcreteCommand):複数のコマンドをまとめて実行できるようにした、CreateFile, DeleteFile, CopyFileのコマンドを集約するクラス。

まず、すべてのコマンドのインターフェイスを規定するCommandクラスです。 このクラスで定義した#executeメソッド#undo_executeメソッドをCreateFile, DeleteFile, CopyFileが持っています。

1
2
3
4
5
6
7
8
9
10
11
12
13
# コマンドのインターフェース
class Command
  attr_reader :description
  def initialize(description)
    @description = description
  end

  def execute
  end

  def undo_execute
  end
end

次にCreateFileクラス, DeleteFileクラス, CopyFileクラスです。各クラスの共通した特徴は次の通りです。

* 各クラスはCommandクラスを継承したConcreteCommand
* #executeメソッド:ファイル作成、ファイル削除、ファイルコピーを実装
* #undo_executeメソッド:ファイル作成、ファイル削除、ファイルコピーを取り消す

なお、最初にrequireしているfileutilsは、ファイルを操作するためのライブラリです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
require "fileutils"

# ファイルを作成する命令
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 undo_execute
    File.delete(@path)
  end
end

# ファイルを削除する命令
class DeleteFile < Command
  def initialize(path)
    super("Delete file : #{path}")
    @path = path
  end

  def execute
    if File.exists?(@path)
      @content = File.read(@path)
    end
    File.delete(@path)
  end

  def undo_execute
    f = File.open(@path, "w")
    f.write(@contents)
    f.close
  end
end

# ファイルをコピーする命令
class CopyFile < Command
  def initialize(source, target)
    super("Copy file : #{source} to #{target}")
    @source = source
    @target = target
  end

  def execute
    FileUtils.copy(@source, @target)
  end

  def undo_execute
    File.delete(@target)
    if(@contents)
      f = File.open(@target, "w")
      f.write(@contents)
      f.close
    end
  end
end

最後にCreateFileクラス, DeleteFileクラス, CopyFileクラスを組み合わせて実行できるようにしたCompositeCommandクラスです。 このクラスもCommandを継承している、ConcreteCommandの一つです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 複数のコマンドをまとめて実行できるようにしたオブジェクト
class CompositeCommand < Command
  def initialize
    @commands = []
  end

  def add_command(cmd)
    @commands << cmd
  end

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

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

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

コーディングは以上です。実際に動かしてみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
command_list = CompositeCommand.new
command_list.add_command(CreateFile.new("file1.txt", "hello world\n"))
command_list.add_command(CopyFile.new("file1.txt", "file2.txt"))
command_list.add_command(DeleteFile.new("file1.txt"))

command_list.execute
puts(command_list.description)
#=> Create file : file1.txt
#=> Copy file : file1.txt to file2.txt
#=> Delete file : file1.txt

# 処理を取り消すコマンド
command_list.undo_execute
#=> file2が消えている

このように使う側はCommandの本当の実装は知りませんが、ファイルの作成、ファイルのコピー、ファイルの削除のコマンドを実行できました。

このサンプルソースはGitHubにも置いています。

サンプルソース(GitHub)

Special Thanks

Command - Murayama blog.

矢沢久雄の早わかりGoFデザインパターン(10)

22.Commandパターン | TECHSCORE(テックスコア)

Amazon.co.jp: Rubyによるデザインパターン: Russ Olsen, ラス・オルセン, 小林 健一, 菅野 裕, 吉野 雅人, 山岸 夢人, 小島 努: 本

変更来歴

12/10 09:00 GitHubへのサンプルソースの設置。導入文の修正
12/11 00:00 書籍へのリンクをAmazon アフィリエイトに変更
12/13 10:55 ソースコードへの説明を追加
06/20 20:00 Ruby2.0.0対応、読みづらい部分を修正

おすすめの書籍