0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonで実装するCommandパターン:操作をオブジェクトに封じ込めて履歴・Undo・キュー処理を可能にする

Posted at

概要

Command(コマンド)パターンは、
操作(コマンド)をオブジェクトとして抽象化し、実行、取り消し(Undo)、履歴、バッチ処理などを構造的に制御できるようにするデザインパターンである。

操作の実行者と実装を分離することで、柔軟な処理制御・操作の記録・非同期実行などに対応可能


1. なぜCommandが必要か?

❌ 呼び出し元が処理内容とその制御すべてを抱え込んでしまう

def execute():
    save()
    undo_stack.append("save")

操作の記録・再実行・Undoが煩雑になり、拡張性が低下


✅ 操作自体をオブジェクト化し、履歴管理や制御の仕組みに統合

cmd = SaveCommand(editor)
invoker.execute(cmd)

すべての操作をオブジェクトで統一的に管理可能


2. 基本構造

✅ Commandインターフェース

class Command:
    def execute(self):
        raise NotImplementedError

    def undo(self):
        raise NotImplementedError

✅ Receiver(処理対象)

class Editor:
    def __init__(self):
        self.content = ""

    def write(self, text):
        self.content += text
        print(f"書き込み: {text} 現在: {self.content}")

    def delete_last(self):
        removed = self.content[-1]
        self.content = self.content[:-1]
        print(f"削除: {removed} 現在: {self.content}")

✅ ConcreteCommand(操作の具象クラス)

class WriteCommand(Command):
    def __init__(self, editor: Editor, text: str):
        self.editor = editor
        self.text = text

    def execute(self):
        self.editor.write(self.text)

    def undo(self):
        for _ in self.text:
            self.editor.delete_last()

✅ Invoker(操作の発行と履歴管理)

class CommandInvoker:
    def __init__(self):
        self.history = []

    def execute(self, command: Command):
        command.execute()
        self.history.append(command)

    def undo(self):
        if self.history:
            cmd = self.history.pop()
            cmd.undo()

✅ 使用例

editor = Editor()
invoker = CommandInvoker()

cmd1 = WriteCommand(editor, "Hi")
cmd2 = WriteCommand(editor, "!")

invoker.execute(cmd1)
invoker.execute(cmd2)
invoker.undo()  # 「!」を削除
invoker.undo()  # 「Hi」を削除

出力:

書き込み: Hi 現在: Hi  
書き込み: ! 現在: Hi!  
削除: ! 現在: Hi  
削除: i 現在: H  
削除: H 現在: 

3. Python的応用:ラムダ関数での簡易コマンドパターン

class LambdaCommand(Command):
    def __init__(self, do, undo):
        self._do = do
        self._undo = undo

    def execute(self):
        self._do()

    def undo(self):
        self._undo()

軽量な処理ならラムダで即座にCommand化可能


4. 実務ユースケース

✅ テキストエディタのUndo/Redo処理

→ 操作の記録と巻き戻しをCommandで管理


✅ GUIボタンアクションのカプセル化

→ ボタンにCommandをバインドすることで動的制御が可能


✅ スケジューラや非同期処理のキューイング

→ 各操作をCommandとして扱うことで順次実行・並列処理が容易に


✅ マクロ・バッチ処理

→ 複数のCommandを順にまとめて実行可能


5. よくある誤用と対策

❌ Commandが受け取り手(Receiver)に過度に依存

→ ✅ Commandは受け取り手を隠蔽しつつ、責務を限定的に持つ


❌ InvokerがCommandの中身に干渉してしまう

→ ✅ InvokerはCommandの実行とUndo呼び出しだけを担う


❌ Commandを設計せず、ただの関数呼び出しに留める

→ ✅ 「操作をオブジェクト化する」ことがこのパターンの要点


結語

Commandパターンとは、“操作という振る舞いをオブジェクトとして制御する設計”である。

  • 実行・取り消し・履歴管理といった処理制御を、構造的かつ拡張可能に管理
  • 操作の抽象化により、UI・非同期・マクロ・バッチ処理との統合が容易
  • Pythonでは関数/ラムダ/クラスを柔軟に使い分け、Commandの表現力を自在に操れる

Pythonicとは、“動作をデータとして管理すること”。
Commandパターンはその操作の知性を、設計の記録性と制御性に変換する技法である。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?