Command パターン

コマンドパターンとは



コマンドパターンは、オブジェクト指向プログラミングにおいて、命令や動作をオブジェクトとして表現するデザインパターンです。リクエストに必要な手続きとデータを「Commandオブジェクト」としてカプセル化し、必要に応じて実行(execute)する点が特徴です。オブジェクトの特性を活かし、命令のキューイング、ロギング、アンドゥ機能などを実現できます。また、処理の実行を`execute()`メソッドに委譲することで、手続きと実行を疎結合にできるという利点があります。

コマンドパターンの定義



コマンドパターンは、以下のステップでリクエストを実行します。

1. Commandクラスの定義: 処理をメソッドとして内包するCommandクラスを定義します。
2. Commandオブジェクトの生成: 定義されたCommandクラスに基づいて、Commandオブジェクトを生成します。
3. `execute()`メソッドの呼び出し: Commandオブジェクトの`execute()`メソッドを呼び出して、リクエストを実行します。

このように、リクエストを「手順書」の定義・生成とその「実行」に段階分けするのが、コマンドパターンの基本的な考え方です。

Commandが内包する処理は開発者に委ねられています。すべての処理をCommand内に記述するパターンや、Commandインスタンス生成時に実行者(Receiver)を受け取り、`execute()`時にReceiverの`action()`を呼び出すパターンがあります。後者のパターンでは、Commandは実行をReceiverに委譲する役割に徹します。

Commandは、Action、Transaction、Taskなどと呼ばれることもあります。

コマンドパターンの利点



コマンドパターンには、主に2つの利点があります。

1. リクエスト処理と実装処理の分離 (疎結合)


例えば、ボタンクリックでリクエストを実行するUIフレームワークを開発する場合を考えてみましょう。フレームワーク側はボタンクリックに応じてリクエストを発行しますが、リクエストに対する処理内容はアプリケーション側に委ねられます。ここで、ボタン生成時にCommandオブジェクトを受け入れるようにすれば、フレームワーク側はCommandの中身を知らなくても、`execute()`メソッドを呼び出すだけでリクエストを実行できます。

このように、コマンドパターンは、処理の実行と実装を疎結合にすることができます。これは、CommandオブジェクトのDI(依存性注入)による処理と実行の分離(関心の分離)と捉えることもできます。この考え方は、手続き型プログラミングにおけるコールバックに相当します。

2. Commandオブジェクトの独立性


Commandは独立したオブジェクトであるため、未実行のCommandオブジェクトを配列に入れることでキューイングが可能です。これにより、非同期処理やスケジューリングが容易になります。また、実行済みのCommandをキューイングすれば、履歴保存やロギング、アンドゥ機能なども実現できます。

コマンドパターンの利用例



印刷処理の例


印刷処理を例に考えてみましょう。手続き的に実装する場合は、印刷設定を引数に取り印刷を実行する関数を作成します。

一方、コマンドパターンで実装する場合は、`execute()`内に印刷処理を含むPrintJob Commandを用意します。ユーザーは印刷時にPrintJobオブジェクトを作成し、印刷ドキュメントや部数などのパラメータをセットし、プリンターへのPrintJob送信メソッドを呼び出します。プリンター側はCommandをキューイングし、準備ができ次第`printJob.execute()`を実行します。

コマンドパターンを利用する利点は、以下の通りです。

コマンド情報の保持: Job名や機能呼び出しユーザーの情報を保持、参照できる
印刷Job全体の情報提供: キューイングに利用した予測時間を提供できる

コマンドオブジェクトの活用例



コマンドオブジェクトは、以下のような機能を実現するのに役立ちます。

複数回のアンドゥ/リドゥ: プログラム内のユーザー操作をコマンドオブジェクトとして実装すれば、実行されたコマンドのスタックを保持できます。アンドゥ機能は、スタックからコマンドをポップして`undo()`メソッドを実行することで実現できます。
トランザクション: アンドゥが途中で失敗した場合に前の状態に戻す(ロールバック)ことが可能です。インストーラーやデータベースはこの機能が必要です。コマンドオブジェクトは2相コミットの実現にも使用できます。
プログレスバー: 実行するコマンドの合計時間を見積もり、全体の進行状況を表示できます。
ウィザード: GUIの設定情報をコマンドオブジェクトに保存し、最後に`execute()`を呼び出して処理を実行します。これにより、UIコードとアプリケーションコードを分離できます。
GUIのボタンとメニュー: JavaSwingDelphiのVCL、WPFなど、多くのフレームワークでコマンドパターンが利用されており、GUIコンポーネントと操作処理を関連付けることができます。
スレッドプール: スレッドプールにコマンドオブジェクトを追加して、非同期処理を実行できます。
マクロ記録: ユーザー操作をコマンドオブジェクトとして記録し、後で再生できます。
ネットワーク: コマンドオブジェクトをネットワーク経由で送信し、他のマシンで実行できます。
並列処理: コマンドを複数のスレッドで並列実行し、処理の高速化を図れます。
移動可能なコード: コマンドをネットワーク経由で移動させ、遠隔地で実行できます。

まとめ



コマンドパターンは、処理の実行をオブジェクトに委譲することで、柔軟性、保守性、拡張性を高めるための強力なデザインパターンです。特にUIアプリケーションや非同期処理、トランザクション処理などを実装する際に有用です。このパターンを理解することで、より洗練されたオブジェクト指向プログラミングが可能になるでしょう。

参考文献


John Gamma, et al. (1994). Design Patterns: Elements of Reusable Object-Oriented Software. pp.263-273. Addison-Wesley Professional. (GoF本)

関連項目



クロージャ
Model View Controller
関数オブジェクト
* デザインパターン

もう一度検索

【記事の利用について】

タイトルと記事文章は、記事のあるページにリンクを張っていただければ、無料で利用できます。
※画像は、利用できませんのでご注意ください。

【リンクついて】

リンクフリーです。