「個々の処理を1つのコマンドとしてまとめる」パターンです。
これにより、個々の処理の再利用化がやりやすくなります。
処理をコマンド(オブジェクト)としてまとめているため、処理の履歴管理やUndo機能を実装しやすくなるというメリットもあります。
Rubyによるデザインパターンの例を一部使用します。
の3つの機能を作ります。
の機能も作ります。
Commandパターンは関数は第一級オブジェクトを使用するとかなり簡易に書くことができるので、サンプルコードでそのように書いています。
import os
import abc
import shutil
foldername1 = "__temp1"
foldername2 = "__temp2"
def main():
# ①: move command
print("step1: do nothing")
print("folder1: ", os.listdir(foldername1))
print("folder2: ", os.listdir(foldername2))
# main command execute
command_mv = CompositeCommand()
command_mv.append_command(CopyCommand("__temp1/hello1.txt", "__temp2/hello1.txt"))
command_mv.append_command(DeleteCommand("__temp1/hello1.txt"))
print("step2: do move")
command_mv.execute()
print("folder1: ", os.listdir(foldername1))
print("folder2: ", os.listdir(foldername2))
print("step3: undo move")
command_mv.unexecute()
print("folder1: ", os.listdir(foldername1))
print("folder2: ", os.listdir(foldername2))
# ②: make and backup command
print("step1: do nothing")
print("folder1: ", os.listdir(foldername1))
print("folder2: ", os.listdir(foldername2))
command_make_and_backup = CompositeCommand()
command_make_and_backup.append_command(CreateCommand("__temp1/hello2.txt", "hello2"))
command_make_and_backup.append_command(CopyCommand("__temp1/hello2.txt", "__temp1/hello2.txt.backup"))
print("step2: do make and backup")
command_make_and_backup.execute()
print("folder1: ", os.listdir(foldername1))
print("folder2: ", os.listdir(foldername2))
print("step3: undo make and backup")
command_make_and_backup.unexecute()
print("folder1: ", os.listdir(foldername1))
print("folder2: ", os.listdir(foldername2))
# Command: 抽象クラス
class Command(metaclass=abc.ABCMeta):
@abc.abstractmethod
def execute(self):
pass
@abc.abstractmethod
def unexecute(self):
pass
# ConcreteCommand: 作成用のコマンド
class CreateCommand(Command):
def __init__(self, path, contents):
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)
# ConcreteCommand: 削除用のコマンド
class DeleteCommand(Command):
def __init__(self, path):
self.path = path
self.contents = None
def execute(self):
f = open(self.path, "r")
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()
# ConcreteCommand: コピー用のコマンド
class CopyCommand(Command):
def __init__(self, path_from, path_to):
self.path_from = path_from
self.path_to = path_to
def execute(self):
shutil.copyfile(self.path_from, self.path_to)
def unexecute(self):
os.remove(self.path_to)
# Invoker(起動者)
class CompositeCommand(Command):
def __init__(self):
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()
if __name__ == "__main__":
main()
大事なのは以下の部分です。
def main():
...
command_mv = CompositeCommand()
command_mv.append_command(CopyCommand("__temp1/hello1.txt", "__temp2/hello1.txt"))
command_mv.append_command(DeleteCommand("__temp1/hello1.txt"))
print("step2: do move")
command_mv.execute()
...
# Invoker(起動者)
class CompositeCommand(Command):
def __init__(self):
self.commands = []
def append_command(self, cmd):
self.commands.append(cmd)
def execute(self):
for cmd in self.commands:
cmd.execute()
...
Pythonの場合、関数は第一級オブジェクトのため、
コマンドを配列に入れ、それを順に実行(または逆順に実行でundo)ができます。
サンプルコードで関数は第一級オブジェクトを使用しています。
サンプルコードと違い、Receiver (受信者)がいます。
これは、Commandの処理対象となるオブジェクトのインタフェースなのですが、
Pythonの特性により、サンプルコードではReceiverを限定する必要がなくなっています。
Pythonの特性により、Receiverを限定する必要がなくなっています。
Wikipedia(クラス図をお借りしました)