1

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

4

6 に答える 6

0

だから私はコードを実行しました、そして私の大きな驚きに、結果はきちんと印刷された「処理イベント」です。興味深いことに、コンポーネント変数のタイプをSomeComponentに変更すると、「HandlingUnitGainedEvent」が出力されます。何らかの理由で、システムは、SomeComponentのhandleEvent(UnitGainedEvent)をオーバーロードする代わりに、ComponentクラスのhandleEvent(Event)メソッドを盲目的に呼び出します。

オーバーロードは実際にはそのようには機能しません。スーパークラスではなく、サブクラスでオーバーロードされたメソッドを呼び出す必要があります。コンポーネントタイプを参照しているため、Javaは、サブクラスでメソッドがオーバーロードされたことを認識せず、オーバーライドしたかどうかのみを認識します。

汎用のhandleEvent(Event)クラスが本当に必要ですか?代わりに、Eventスーパークラスのハンドラーがなくても、特定のEventサブクラスのhandleEvent()メソッドを記述できますか?

于 2009-02-26T00:38:04.660 に答える
0

上記の例では、基本クラスからメソッドをオーバーロードしていますが、オーバーライドしていません。クラスでを処理する抽象(または空)メソッドを定義した場合、期待どおりに機能UnitGainedEventしますComponent

別の角度から問題を攻撃することを検討することをお勧めします。採用できるアプローチの1つは、あらゆる種類のリスナーを使用してSwingで実行されるのと同様のことを実行することです。イベントを論理的にグループ化し、それらのイベントに多数のリスナーを提供できます。次に、各グループ内でアダプターを提供して、クライアントが関心のある特定のイベントのみを処理するアダプターをオーバーライドできるようにします。

于 2009-02-26T00:39:29.960 に答える
0

Javaでサポートされていない共変引数を探しているようです(ただし、共変戻り型はサポートされています)。

オブザーバーパターンを使ってみませんか?コンポーネントが常に特定のタイプのイベントに関心がある場合は、静的レジストリを作成して、そのタイプのイベントが発生するたびに、関心のあるすべてのコンポーネントに通知されるようにすることができます。

于 2009-02-26T00:40:08.350 に答える
0

ビジターパターンが正しい解決策です。基本的に必要なのは多重ディスパッチです。コンポーネントのタイプとイベントのタイプの両方に基づいてメソッドを選択する必要がありますが、これはJavaでは不可能です。詳細については、このウィキペディアの記事を参照してください:[ http://en.wikipedia.org/wiki/Multiple_dispatch#Java][1]

[1]:http:// Multiple Dispatch

于 2009-02-26T00:41:08.503 に答える
0

何らかの理由で、システムは、SomeComponentのhandleEvent(UnitGainedEvent)をオーバーロードする代わりに、ComponentクラスのhandleEvent(Event)メソッドを盲目的に呼び出します。(この背後にあるSunの推論を聞くことに興味がありますが、それは私の質問にはあまり関係がないと思いました。少数の人々がそれを非常に便利な機能だと思うからといって、彼らがそれを修正するわけではありません。)

コンパイラは「ばか」です。このようにコードを変更すると、次のようにする必要がある理由がわかります。

パブリッククラスメイン
{{
    public static void main(String [] args)
    {{
        コンポーネントcomponent=new SomeComponent();
        イベントevent=foo();

        component.handleEvent(event);
    }
}

foo()はある種のイベントを返しますが、何がわかりません。コンパイラは返されるサブクラスを知ることができず、コンポーネントがそのメソッドをサポートしている場合、コンパイラが実行できるのは、知っていることを処理することだけです。つまり、コンポーネントクラスにはイベントを受け取るhandleEventがあります。

実行しているいくつかの場合は、java.util.Eventタイプとjava.util.EventObjectタイプを確認する必要があります。

于 2009-02-26T00:42:23.587 に答える
0

反射を使用して、目的の効果を取得します。イベントをリスナーにディスパッチするときは、そのメソッドを調べて、最適な一致を見つけます (コンパイラーに期待していたこと)。

呼び出しターゲットをキャッシュしたいのは明らかです。そうしないと、パフォーマンスが低下する可能性があります。

于 2009-02-26T01:12:54.480 に答える