この記事には複数の問題があります。改善
やノートページでの議論にご協力ください。Command パターン(英: command pattern)は、オブジェクト指向プログラミングにおいて命令あるいは動作をオブジェクトで表現するデザインパターンの一種である。
リクエストのために必要な手続きとデータをCommandオブジェクトとしてカプセル化した上で取り回し[1]、必要に応じて実行(execute)するパターンである。オブジェクトであることを生かして命令のキューイングやロギング、アンドゥ(undo)等が可能になり[2]、実際に処理を実行するサブルーチンを、オブジェクトに属するメソッドexecute()として分離することによって、手続きと実行を疎結合にできる。 Commandパターンでは何かリクエストを実行する際、単純に処理を実行するのではなく、次のステップを踏む。 すなわちリクエストを「手順書」の定義・生成とその「実行」に段階分けするパターンをとる。 Commandが内包する処理の記述は開発者に一任されている。全ての処理をCommand内に記述するパターン[4]、あるいはcommandインスタンス生成時に実行者(Receiverクラスのインスタンスreceiver、受信者もしくは観測者とも)を受け取りcommand.execute()時にreceiver.action()コールのみをおこなう、すなわち実行をReceiverに委譲してCommandは繋ぎに徹するパターンもある[5]。なお、Receiverは特にCommandパターンの要件というわけではない。 CommandはAction、Transaction[6]あるいはTask[7]とも呼ばれる。 このパターンがもつ利点の1つはリクエスト依頼処理と実装処理の分離(疎結合)である[8]。 例えばボタンのクリックでリクエストを実行できるUIフレームワークを開発するとする。ボタンのクリック機能とリクエストの実行はフレームワークの責務だが、リクエストの実行によって具体的に何が起こるかはアプリケーションの責務である。すなわちUIフレームワーク側はクリックに応じてリクエストを発行するが、リクエストに対してどのような処理がおこなわれるのか、そもそもリクエストの受け手が誰なのかについては関知しない[9]。ここでボタンの生成時にCommandを受け入れるとする。UIフレームワーク側はCommandがどんな処理を内包しているかは(カプセル化されているので)わからないが、execute()メソッドを実行すればリクエストが実行されることは知っている。このインターフェイス(interface)を介した契約により、クリック時にcommand.execute()するだけでクリックに応答したリクエストを実行できる。このようにCommandパターンはCommandの実行と実装を疎結合にできる。言い換えれば、CommandパターンはCommandオブジェクトのDIによる処理と実行の分離(関心の分離)である。これは手続き型プログラミングにおけるコールバック(callback)に相当する[10]。 このパターンがもつもう1つの利点はCommandが独立したオブジェクトである点である。未実行のCommandオブジェクトを配列にいれればキューイングが可能であり、それに応じた非同期処理・スケジューリングが可能になり、実行済みのCommandをキューイングすれば履歴保存とロギング・アンドゥなどが可能になる。 この節には独自研究が含まれているおそれがあります。問題箇所を検証し出典を追加して、記事の改善にご協力ください。議論はノート 例としてプリンターによる印刷を考える。 手続き的に実装する場合、印刷設定を引数にとり印刷を実行するSendJobToPrinter 関数を叩けばよい。 Commandパターンで実装する場合、.Execute() 内に印刷手続きを含むPrintJob Commandを用意する。ユーザーは印刷時にPrintJob オブジェクトを作成し、印刷するドキュメント・印刷部数などのパラメータをCommandへセット、最後にプリンターへのPrintJob 送信メソッドを呼び出す。プリンター側はCommandをキューイングし準備が出来次第printJob.Execute()を実行する。 印刷にCommandパターンを利用する利点は以下である。 この節は検証可能な参考文献や出典が全く示されていないか、不十分です。出典を追加して記事の信頼性向上にご協力ください。(このテンプレートの使い方)
定義
処理をメソッドとして内包するCommandクラスの定義
Commandオブジェクトの生成
Command.execute()メソッドのコールによるリクエスト実行[3]
利点
利用例
コマンド情報の保持: Job名や機能を呼び出したユーザーの情報を保持、参照
印刷Job全体の情報提供: キューイングに利用した予測時間の提供
活用例
出典検索?: "Command パターン"
Command オブジェクトは下記のような機能を実現するのに便利である。
複数回のアンドゥ(英語版)
プログラム内の全てのユーザーの操作がコマンドオブジェクトとして実装されれば、プログラムは最近実行されたコマンドのスタックを保持することができるようになる。ユーザーがコマンドをアンドゥしたい場合、プログラムは最後に実行されたコマンドオブジェクトを単純にポップし、そのコマンドオブジェクトの undo() メソッドを実行する。さらにアンドゥスタックを別途用意することで、アンドゥされたコマンドを再実行するリドゥ機能を実現することもできる。一般的なドキュメント編集用アプリケーションでは、ドキュメントに対する編集操作に関して、「元に戻す」「やり直し」といったメニューコマンドによってこれらの機能が提供されている。
トランザクション的振る舞い
アンドゥの操作が途中で失敗し、前の状態に巻き戻すこと(ロールバック)を要求されたときに不可欠である。インストーラやデータベースはこの機能を必要とする。コマンドオブジェクトは2相コミットの実現にも使用できる。
プログレスバー
プログラムが一連のコマンドを順番に実行する場合を考える。各コマンドオブジェクトが getEstimatedDuration() を持っていれば、プログラムは全体の処理時間を簡単に予測することができる。全てのタスクの完了がどの程度近いのかを意味のある形で示すプログレスバーを表示できる。
ウィザード
ウィザードはあるアクションの設定のためにいくつかのページを表示し、最後のページで完了ボタンを押すまでアクションは実行されないことが多い。こうした場合、ユーザーインターフェイスのコードとアプリケーションのコードを分離するための自然な方法として、コマンドオブジェクトを用いたウィザードを作成することがある。コマンドオブジェクトはウィザードが最初に表示される際に作成される。ウィザードの各ページはコマンドオブジェクト内に GUI で行われた変更を保存する。完了は、execute() を呼び出すだけである。このようにしてコマンドオブジェクトの元になるクラスには、一切ユーザーインターフェイスコードを含まないようにすることができる。
GUI のボタンとメニューの項目
JavaにおけるアプリケーションフレームワークのひとつSwingでは、インターフェイスjavax.swing.Actionを実装する任意のクラスのインスタンスがコマンドオブジェクトである。目的のコマンドを実行する能力に加え、Action は関連するアイコンやキーボードショートカット、ツールチップのテキストなどを持つことができる。ツールバーのボタンやメニューのコンポーネントは Action オブジェクトのみを使って完全に初期化することができる。DelphiのVisual Component Library (VCL) では基底クラスVcl.ActnList.TActionが用意されている[11]。WPFにはSystem.Windows.Input.ICommandインターフェイスが用意されているが、WPFはさらにMVVMパターンを活用した強力なバインディングフレームワークも備えており、XAML上でGUI要素にコマンドを関連付けることもできる[12]。
スレッドプール