RTSゲーム用のAIを構築しています。(Spring RTSエンジンの場合、誰かが興味を持っている場合。)私が設定した方法は、主に、発生したイベントによって通信するコンポーネントのコレクションで構成されています。すべてのコンポーネントには、他のコンポーネントによって発生したイベントを受け取るメソッドhandleEvent(Event)があります。
イベントはインターフェースです。階層が存在し、各サブクラスは、それらが表すイベントに関するより詳細な情報を提供します。これらの情報には、そのクラスに固有のgetterメソッドを使用してアクセスできます。例として、クラスUnitGainedEventはEventインターフェースを実装します。このクラスには、サブクラスUnitFinishedEventがあります(興味のある人のために、ユニットまたは建物の建設が完了したことを示します)。
すべてのコンポーネントがすべてのイベントに関心があるわけではないので、コンポーネントが関心のあるイベントを選択し、それらだけを受け取るようにします。また、可能なイベントのセットは拡張可能であるため、コンポーネントにイベントタイプごとに指定されたメソッドを持たせることは有効なオプションではありません。当初、ビジターパターンはこの問題に役立つかもしれないと思っていましたが、固定されたイベントタイプのセットも必要なため、失敗します。(パターンが正しく理解されているかどうかは100%わかりません。間違っている場合は修正してください。)
これまでのところ、私が見つけた唯一の解決策は、各コンポーネントのhandleEvent(Event)メソッドを次のように実装することです。
public int handleEvent(Event event)
{
if (event instanceof UnitGainedEvent)
{
UnitGainedEvent unitGainedEvent = (UnitGainedEvent) event;
// things to do if I lost a unit
}
else if (event instanceof UnitLostEvent)
{
UnitLostEvent unitLostEvent = (UnitLostEvent) event;
// things to do if I lost a unit
}
// etc.
}
ただし、特定のイベントクラスにイベントをキャストする必要はありません。ここで、メソッドのオーバーロードを使用して、パラメーターの実行時型に応じてさまざまなメソッドを呼び出すことができることを思い出して、シンプルでエレガントな素晴らしいソリューションをすぐに思いつきました。handleEvent(の空の実装で基本クラスを作成できました。イベント)、たとえば、メソッドhandleEvent(UnitGainedEvent unitGainedEvent)を作成することにより、サブクラスに関心のあるイベントを受信させるだけです。それが機能することを確認したいので、簡単なテストケースを設定しました。
public class Main
{
public static void main(String[] args)
{
handleEvent(new UnitGainedEvent(null));
}
public static void handleEvent(Event event)
{
System.out.println("Handling generic Event");
}
public static void handleEvent(UnitGainedEvent event)
{
System.out.println("Handling UnitGainedEvent");
}
}
そして、私の非常に満足のいくように、このコードは実際に「HandlingUnitGainedEvent」を出力します。そこで、実装に着手しました。
私のComponent基本クラスは次のようになります:(実際にはそうではありません。これは、私が示したい問題に関係のないすべてのものを取り除いたComponentクラスです。)
public class Component
{
public void handleEvent(Event event)
{
System.out.println("Handling Event");
}
}
そして、これはサブクラスの例です:
public class SomeComponent extends Component
{
public void handleEvent(UnitGainedEvent unitGainedEvent)
{
System.out.println("Handling UnitGainedEvent");
}
}
セットアップをテストするために、次のメインクラスを使用します。
public class Main
{
public static void main(String[] args)
{
Component component = new SomeComponent();
component.handleEvent(new UnitGainedEvent(null));
}
}
だから私はコードを実行しました、そして私の大きな驚きに、結果はきちんと印刷された「処理イベント」です。興味深いことに、コンポーネント変数のタイプをSomeComponentに変更すると、「HandlingUnitGainedEvent」が出力されます。何らかの理由で、システムは、SomeComponentのhandleEvent(UnitGainedEvent)をオーバーロードする代わりに、ComponentクラスのhandleEvent(Event)メソッドを盲目的に呼び出します。(この背後にあるSunの推論を聞くことに興味がありますが、それは私の質問にはあまり関係がないと思いました。少数の人々がそれを非常に便利な機能だと思うからといって、彼らがそれを修正するわけではありません。)
ネットを精査すると、他の人も同じ問題に遭遇したことがわかります。私が見つけたわずかな量の情報からすると、非常に少数の人ですが、それでも、一般的なメソッドのオーバーロードとオーバーライドに関する情報は、私が知りたいと思っていたよりも多く見つかりました。しかし、結局、私は解決策を見つけることができません。
さて、私の(かなり明白な)質問は、この問題を回避する方法はありますか?それができない場合、誰かが同じように便利な別の解決策を考えたり、見つけたりするのを手伝ってくれるでしょうか?
編集:たった10分で答えがあるなんて信じられません。嬉しい驚きです。:)ただし、これまでの回答のほとんどは、考えられるイベントタイプごとに1つの個別のメソッドを作成することを何らかの形で示唆しています。技術的にはこれは可能ですが、誰かが新しいイベントタイプを思い付くたびに、コードに戻って新しいメソッドを追加する必要があります。私が学んだのは、悪いコーディング慣行です。(さらに、私はすでに20以上のイベントタイプを持っており、まだ完了していません。)代わりに、上記のように、キャストを含むソリューションを使用したいと思います。少なくともそうすれば、未知のイベントタイプが単に無視されることを保証でき、それらを使用したいイベントのみを自由に処理できるようになります。しかし、私は両方の長所を組み合わせたソリューションを本当に望んでいます。キャスティングなし、
よろしくお願いします、Loid Thanead