Commandパターン
『Rubyによるデザインパターン』(ラス・オルセン著 ピアソン・エデュケーション刊)の例をPythonに変換して書いています。
目次
ボタンが押されたとき、何かのタスク(ビープ音を鳴らす、メールの送信、ファイルの保存etc)
を実行しなければなりませんが、それをどうやって指定すればよいのでしょうか?
継承を使うのも一つの手です。TemplateMethodパターンです。
しかし、微妙に違うボタン毎にクラスを作りたくはありません。(200Hzの音を鳴らすボタン、220Hzの音を鳴らすボタン、240Hzの・・・)そこで「ボタンが押されたときの動作」を表現するクラスを作ります。
Commandパターンです。
class SlickButton(object): def __init__(self, command): self.command = command #描画等の処理のコード・・・ def on_button_push(self): self.command.execute() class BeepCommand(object): def execute(): #音を鳴らす処理・・・ class SendMailCommand(object): def execute(): #メール送信処理・・・ cmd = BeepCommand() btn = SlickButton(cmd) cmd2 = SendMailCommand() btn2 = SlickButton(cmd)
音を鳴らしたりメールを出したりといった処理は、コマンドオブジェクトに任せ、
ボタンはボタン自体の処理に集中します。
アンドゥ・リドゥをするコマンド
Commandパターンを使うと、アンドゥ・リドゥを簡単に実装できます。
たとえばインストーラの場合、
- ディレクトリ新規作成
- ファイル新規作成
- ファイルコピー
- ファイル移動
- ファイル削除
ファイルを作った直後に、そのファイルを削除するのは簡単です。
ファイルを移動した直後に、元の場所に戻すのも簡単です。
コマンドは、自分が実行された後、それを帳消しにする方法を知っているのです。
import os class Command(object): def __init__(self, _description): self._description = _description def description(self): return self._description def execute(self):pass def unexecute(self):pass class CreateCommand(Command): def __init__(self, path, contents): Command.__init__(self, "Create file: %s"%(path,)) self.path = path self.contents = contents def execute(self): f = open(self.path, "w") f.write(self.contents) f.close() def unexecute(self): os.remove(self.path) class DeleteCommand(Command): def __init__(self, path): Command.__init__(self, "Delete file: %s"%(path,)) self.path = path self.contents = None def execute(self): try: f = open(self.path, "r") except IOError: pass else: self.contents = f.read() f.close() os.remove(self.path) def unexecute(self): if self.contents is not None: f = open(self.path, "w") f.write(self.contents) f.close() class CompositeCommand(Command): def __init__(self): Command.__init__(self, None) self.commands = [] def description(self): return "\n".join(cmd.description() for cmd in self.commands) def append_command(self, cmd): self.commands.append(cmd) def execute(self): for cmd in self.commands: cmd.execute() def unexecute(self): for cmd in reversed(self.commands): cmd.unexecute()
CreateCommandは、execute/unexecuteでファイルを作成/削除、
DeleteCommandは、execute/unexecuteでファイルを削除/復元します。
Compositeパターンを使ったCompositeCommandは、append_commandでコマンドを溜め込み、
一気に実行・復元します。