ホーム < ゲームつくろー! < デザインパターン習得編

Command
  〜命令をクラスにしてしまえ!


@ おれは誰に命令してるんだろう?

 例えば、「ファイルを開け」という命令(要求)は、色々なアプリケーションで使用されますね。でも、開くファイルはアプリケーションによって違います。同じ「開け」でも、Excelならエクセルファイル、Wordならワードファイル、Photoshopなら絵のファイルが開かれるはず。「命令は同じなのに結果が異なる」。この辺にクラスの派生の匂いがしませんか?
 Commandパターンは、正にここに注目し、命令をクラスにしてしまって、命令を派生することによって、各アプリケーション専用の結果を出すようにしよう、というパターンです。

 このパターンを最初見たとき、正直私は「どこに利点があるのだろう」と首を傾げました。命令をクラスにすると、何か良いことがあるのでしょうか?「オブジェクト指向における再利用のためのデザインパターン(SOFTBANK Publishing)」を見ると、どうやら「命令する人と実行する人を分けたい」場合に使えるようです。

 命令する人は「ファイルを開け」としか言いません。それを聞く人(Commandオブジェクト)は、自分が知っている実行者に「すいませんがファイルを開いてくれませんか?」と頼みます。その結果実行者の意図するファイルが開きます。結局、命令者は何のファイルが開かれたかは知らないわけです。命令者は、「おれは誰に命令しているんだろう?」と不安に思っているかもしれません(笑)。

 通常命令者は、命令を聞く人を複数人そばに控えています。命令を聞く人を入れ替えれば、色々な実行結果が期待できます。この辺が、このパターンの利点のようです。


A ゲームメニューにCommandパターン

 このパターンをゲーム製作に応用するとしたら、「メニュー」が一番分かりやすいと思います。ゲームにはメニューが欠かせません。メインメニューにはたいてい「Start」「Option」「Quit」などがあります。これは「共通する命令」と考えられます。この命令をまとめている人、これが「命令者(invoker)」です。Startを選択すると、ゲームが始まります。先ほどの話からすると、命令者は何のゲームが始まるかは知らないのです。ここが面白いですよね。とりあえず、模式図で示します。

 メニュー画面の3つの命令(Command: 要求)を保持する命令者(invoker)は、プレーヤーがStartをクリック(選択)するとCommandオブジェクトのExecute関数を実行します。OX_StartCommandオブジェクトのExecute関数は「Game->Start()」という実行を行います。GameにはOXGameApplicationが登録されていて、そのStart関数が実行されます。流れは難しくはないですね。

 ポイントは、命令者は命令の登録機構さえあれば、どんな命令(Commandオブジェクト)も保持できるので、どんなメニューでも構成できる点です。一方、OX_StartCommandオブジェクトなどは、たぶん使い捨てでしょうね。StartCommandクラスを作ればある程度の再利用性は考えられますが、まぁどうなんでしょうという感じです。使い捨てると割り切ってしまった方が良いかもしれません。


 命令者に特定の「メニュー表示機能」を実装すれば、ゲーム内で使いまわすことが可能です。普通、ゲームのメニューというのは統一性が取れていた方がカッコイイもんですから、これは重要だと思います。

ここで、Commandパターンのクラス図を示します。

 見た目は少し複雑なのですが、これをOXゲームのメインメニュー画面に置き換えると、やっていることが良く分かります。


B 登録者は誰?選択したのは何?


 OX_StartCommandオブジェクトを生成し、そこにOXGameApplication(へのポインタ)を登録するのは上に表示するクライアントの役目です。点線の矢印がそれを意味します。では、Invoker(命令者)にOX_StartCommandオブジェクトを登録する人は誰か?

 それは多分上のクライアントなんだろうと思います。
 OXGameApplicationが登録されたOX_StartCommandを知っているのは、きっとClientのみです。ということは、Invokerを生成するのはやはり上のクライアントで、OX_StartCommandを登録するのもその時になるはずです。

 次に、Invokerはプレーヤーの選択をどう判断するのでしょうか?画面に出ているメニューをクリックするというのは、ビューの役目です。抽象的には「上から○番目の欄をクリックした」という情報でしょう。InvokerはそれをCommandオブジェクト配列の要素番号に適切に変換しなければなりません。


C メニューの中のメニュー

 時には、Commandオブジェクトがサブのメニューを持っていて、さらにそのメニューにあるCommandオブジェクトがメニューを持っていて・・・という入れ子が必要になる事もあります。イメージとしてはWindowsのメニューや連続性のあるRPGのメニュー画面です。入れ子といえばCompositeパターン。これは、トップの命令者(Invoke)が出した命令を聞く人がサブの命令者であれば良いわけです。ということは、Commandクラスの派生クラス内にInvokeを持てば、これを実現できることがわかります。


メニュー以外でCommandパターンを適用できる場面は、そう多くは無いように思えます。命令がとても専門的になりやすく、また1つのCommand内で独立・完結した実行が行われなければ、ひどい目にあうパターンでもあるます。複雑になりすぎないよう、使用は慎重に検討するのが無難です。