i-school - インターフェースを利用したコマンドパターンの実装例

インターフェースの機能と定義


 インターフェースは、クラスに対して特定のメソッドやプロパティを実装することを要求することができるものです。
これにより、複数のクラスで同じ動作をすることを強制することができます。

参考サイト
++C++; // 未確認飛行 C 様
インターフェース



 実装の方法は、インターフェースを定義するための「interface」キーワードを使用します。例えば、次のようなインターフェースを定義できます。


<= クリックすると開きます。




 このインターフェースを実装するクラスは、「IMoveable」を実装して、「Move」メソッドを実装することが必要になります。


<= クリックすると開きます。


 インターフェースで定義したメソッドは、明記しなくても public 扱いになります。



 インターフェースの利点としては、次のようなものがあります。

 ・複数のクラスで同じ動作を強制することができる。
 ・インターフェースを継承したクラスに対して同じように操作することができる。
 ・コードの保守性を高めることができる。
 ・仮想メソッドよりも軽量である。

 これらの利点により、インターフェースを利用した設計を行うことでよりスマートなコードを書くことができます。


コマンドパターン


 コマンドパターンは、命令をオブジェクトとして扱うことで、再利用性を向上させ、管理しやすくすることができる設計パターンです。
プログラムにおけるデザインパターンの1つであり、この設計パターンにおいてはインターフェースが活用されています。

 下記に実装例を示します。


1.命令用のインターフェースを定義する


 まず、コマンド用の ICommand インターフェースを定義します。
今回はサンプルのためファイルを分けていますが、1つのファイルにインターフェースとクラスとを一緒に書いても問題ありません。


<= クリックすると開きます。



2.命令用のクラスを定義する


 次に、各命令を表すクラスを実装します。
命令ごとのに1つのクラスを定義するようにします。

 今回は2つのクラスを定義しています。

<移動用の命令クラス>

<= クリックすると開きます。



<ジャンプ用の命令クラス>

<= クリックすると開きます。


 どちらのクラスにも ICommand インターフェースが実装され、クラス内に Execute メソッドの実装が強制されていることが分かります。
ただし、それぞれの Execute メソッドの実装(中身)が異なるため、同名のメソッドではあるものの処理の内容が変わっています


3.命令管理クラスを定義する


 最後に、命令を管理するクラスを作成します。


<= クリックすると開きます。


 AddCommnad メソッドを利用し、ICommnad インターフェースを実装しているオブジェクトを commands リストに登録しています。
ポイントとしては、ICommnad インターフェースのリストであるため、クラスに関わらず、ICommnad インターフェースを実装していればリストに登録できる点です。
そのため、MoveCommnad クラス、JumpCommnad クラスのどちらであっても commands リストに登録できます。

 ExecuteCommands メソッドでは ICommnad のリストである commands リストを foreach 文を利用してループ処理し、
リスト内に格納されている ICommnad インターフェースを実装しているクラスの各 Execute メソッドを実行します。
Execute メソッドは MoveCommand クラスと JumpCommnad クラスではメソッド内の処理が異なるため、同じメソッドを実行していますが、処理の振る舞いが変わります。


4.利用方法


 命令を実行するためには、「CommandManager」クラスに「ICommand」を実装した「MoveCommand」や「JumpCommand」などを追加して行います。
それらを追加したあとに、実際に処理を行う Execute メソッドを実行するには「ExecuteCommands」メソッドを呼び出します。

 「CommandManager」クラスから「MoveCommand」を命令を実行するためのサンプルは以下のようになります。


<= クリックすると開きます。


 このサンプルでは、「Start」メソッド内では「CommandManager」に「MoveCommand」を追加しています。
その後、「Update」メソッド内でスペースキーが押された時に「CommandManager」の「ExecuteCommands」メソッドを呼び出して「MoveCommand」を実行しています。


DI コンテナ(VContainer)を利用したケースでの実装例


 VContainer を利用することで「CommandManager」クラスをDI(Dependency Injection)コンテナとして活用することができます。
以下は、「CommandManager」クラスを「VContainer」を利用して活用する例です。


1.インターフェースの定義


 VContainer の機能を利用するための ICommandManager インターフェースを作成します。


<= クリックすると開きます。



2.命令管理用のクラスの定義


 先ほど作成した CommnadManager とは異なり、ICommnadManager インターフェースを実装して命令管理用のクラスを作成します。


<= クリックすると開きます。


 「CommandManager」クラスは「ICommandManager」インターフェースを実装し、「Inject」属性を付けています。
これにより、「VContainer」に「CommandManager」クラスが「ICommandManager」として登録されます。


3.Example クラスを定義して活用する例


 MoveCommnad クラスなどをそのまま利用し、同じ処理を作成します。


<= クリックすると開きます。


 「Example」クラスは「ICommandManager」をインジェクトするために「Inject」属性を付けています。
「Start」メソッド内では「Inject」メソッドを呼び出すことで「ICommandManager」のインスタンスが生成されます。



 DI コンテナの機能を利用することにより、処理の抽象度をさらに増し、汎用性のある処理に作り変えることが可能です。