
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対応、読みづらい部分を修正