0
0

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パターン:操作をオブジェクトとして抽象化する

Posted at

概要

Command(コマンド)パターンは、
処理内容(操作)をオブジェクトとして抽象化・独立させることで、呼び出しと実行の分離・Undo/Redoの実装・操作の履歴管理を可能にするデザインパターンである。

この構造により、GUIボタン、キーボードショートカット、メニュー操作などの振る舞いを柔軟に管理可能となる。


1. なぜCommandが必要か?

❌ 操作とUIイベントが直結している設計

def on_button_click():
    editor.clear()

→ UIがロジックに強く依存しており、再利用・拡張・テストが困難


✅ 操作をオブジェクトとして切り出し、UIはその実行を委ねる

clear_command = ClearCommand(editor)
clear_command.execute()

操作はCommandオブジェクトとして独立し、複数のトリガーから再利用可能に


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

    def clear(self):
        self.content = ""

    def show(self):
        print(f"[Editor] {self.content}")

✅ ConcreteCommand(具体的なコマンド)

class ClearCommand(Command):
    def __init__(self, editor):
        self.editor = editor
        self._backup = ""

    def execute(self):
        self._backup = self.editor.content
        self.editor.clear()

    def undo(self):
        self.editor.content = self._backup

✅ Invoker(コマンドを管理・実行する役)

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

    def run(self, command: Command):
        command.execute()
        self._history.append(command)

    def undo(self):
        if self._history:
            command = self._history.pop()
            command.undo()

✅ 使用例

editor = Editor()
editor.write("Hello, World!")
editor.show()

invoker = CommandInvoker()
clear = ClearCommand(editor)

invoker.run(clear)
editor.show()

invoker.undo()
editor.show()

出力:

[Editor] Hello, World!
[Editor] 
[Editor] Hello, World!

3. Python的応用:Lambdaベースの簡易コマンド

class LambdaCommand(Command):
    def __init__(self, execute_fn, undo_fn):
        self.execute_fn = execute_fn
        self.undo_fn = undo_fn

    def execute(self):
        self.execute_fn()

    def undo(self):
        self.undo_fn()

小規模な一時的コマンドも柔軟に定義可能


4. 実務ユースケース

✅ GUI操作(ボタン/メニュー/ショートカット)に対する処理の抽象化

同一コマンドを複数UIイベントから呼び出せる


✅ Undo/Redo 機能の実装

各操作を履歴として蓄積し、逆操作を実行


✅ マクロ記録・再生機能

→ コマンドの列を記録・シリアライズ・再実行可能に


✅ リモート実行(キューに蓄積→バッチ実行)

→ 操作内容をオブジェクトとして安全に転送・保存


5. よくある誤用と対策

❌ Commandオブジェクトが複雑な状態を持ちすぎる

→ ✅ 状態は最小限に、処理の委譲先はReceiverに限定する


❌ 実行とUndoが非対称で整合性が取れない

→ ✅ Undo可能な処理のみをCommand化するか、明示的に不可とする


❌ Invokerが複雑なロジックを持つ

→ ✅ Invokerはコマンドの管理と呼び出しだけに集中させる


結語

Commandパターンとは、“操作そのものをオブジェクトとして表現し、自由に扱えるようにする設計手法”である。

  • 操作のカプセル化により、UIとロジックの疎結合化と再利用性の向上を実現
  • Undo/Redo、バッチ処理、リモートコマンドなど、構造化された操作の応用が可能に
  • Pythonではファーストクラスオブジェクトとしての関数やラムダ式により、軽量かつ柔軟にCommand設計が可能

Pythonicとは、“ふるまいもまたデータである”という原則に基づいて構造を抽象化すること。
Commandパターンはその設計思想を、操作としてのコードに昇華させる技法である。

0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?