2011-01-19 22 views
7

以下では、EventHandlerがEventAを一方向に、EventBを別の方法で、他のイベント(EventC、EventD)を別の方法で処理します。 EventReceiverはイベントへの参照のみを受け取り、EventHandler.handle()を呼び出します。もちろん、常に呼び出されるバージョンは、EventHandler.handle(イベントイベント)です。Javaで多態的にディスパッチする

instanceOfを使用しないと、ポリモーフィックに(おそらくEventHandlerまたはジェネリックの別のメソッドを介して)適切なハンドルメソッドにディスパッチする方法がありますか?

class EventA extends Event { 
} 

class EventB extends Event { 
} 

class EventC extends Event { 
} 

class EventD extends Event { 
} 

class EventHandler { 
    void handle(EventA event) { 
     System.out.println("Handling EventA"); 
    } 

    void handle(EventB event) { 
     System.out.println("Handling EventB"); 
    } 

    void handle(Event event) { 
     System.out.println("Handling Event"); 
    } 
} 

class EventReceiver { 
    private EventHandler handler; 

    void receive(Event event) { 
     handler.handle(event); 
    } 
}  

答えて

10

Visitor patternを適用する場合のように聞こえます。 (例えば、C++、C#やJavaなどの主流のオブジェクト指向言語では、方法は、すなわち一度に一種類上の多型であることができる、ディスパッチ単一ある。ビジターは一つダブルディスパッチを実装することを可能にする。)

は、しかし、これは必要Eventクラスも同様に変更でき、Eventから(基本インターフェースの)EventHandlerへの依存関係を作成することができます。

class EventA extends Event { 
    public handleBy(EventHandler eh) { 
    eh.handleEventA(this); 
    } 
} 

class EventB extends Event { 
    public handleBy(EventHandler eh) { 
    eh.handleEventB(this); 
    } 
} 

class EventHandler { 
    void handleEventA(EventA event) { 
     System.out.println("Handling EventA"); 
    } 

    void handleEventB(EventB event) { 
     System.out.println("Handling EventB"); 
    } 

    void handle(Event event) { 
     event.handleBy(this); 
    } 
} 
+1

+1のビジターパターン –

+0

Visitorパターンと非周期的なVistorパターンについて考えました。残念ながら、Eventクラスを変更することはできません。さらに、私は時間の経過とともに多くの特殊なイベントを追加する必要があります。 – user581638

0

マップを使用してイベントタイプをイベントハンドラにマップできます。

Map<Class<? extends Event>, Handler> map = 
    new HashMap<Class<? extends Event>, Handler>(); 

void receive(Event event) { 
    Handler handler = map.get(event.getClass()); 
    handler.handle(event); 
} 
1

これは、(1が実際に知っている可能性としては、どちらかのビジターと呼ばれる)は、double-dispatchのためのユースケースではありませんか?私はEventAのためだけにあなたの例を実装します

class Event { 
    /** 
    * Will do some type escalation 
    */ 
    void handleWith(EventHandler care) { 
     care.handle(this); 
    } 
} 



class EventA extends Event { 
    /** 
    * As event is EventA, this implementation is called, with its correct type forced by the cast 
    */ 
    void handleWith(EventHandler care) { 
     care.handle((EventA) this); 
    } 
} 

class EventHandler { 
    /** 
    * Finally comes here 
    */ 
    void handle(EventA event) { 
     System.out.println("Handling EventA"); 
    } 

    void handle(EventB event) { 
     System.out.println("Handling EventB"); 
    } 

    void handle(Event event) { 
     System.out.println("Handling Event"); 
    } 

    /** 
    * Go here first and dispatch call to Event class 
    */ 
    void doHandle(Event event) { 
     event.handleWith(this); 
    } 
} 

class EventReceiver { 
    private EventHandler handler; 

    void receive(Event event) { 
     handler.doHandle(event); 
    } 
}  
1

Javaは、メソッドが呼び出されるオブジェクト上に多相ディスパッチを持っています。つまり、実際の多形性を得る唯一の方法は、handle()メソッドをEventインターフェイス自体に入れることです。データオブジェクトで動作する「ハンドラ」はむしろ手続き的であるため、全体的にはより優れたOOソリューションと言えます。

他の解決策(クラスにキー入力されたハンドラオブジェクトのマップなど)は、継承に関して、より複雑で柔軟性が低くなります。

+0

私はMichaelに同意します。しかし、イベントは異なるコンテキストで使用することができ、状況に応じてさまざまなことを行う必要があります。 – user581638

0

私はあなたがいくつかの前処理でそれを行う方法を知っています。

public abstract class EventHandler<T extends Event> { 
    public abstract void handle(T event, Class<T> c); 
    public abstract Class<T> handles(); 
} 

public class EventHandlerA extends EventHandler<EventA> { 
    @Override 
    public void handle(EventA event, Class<EventA> c) { 
     System.out.println(event); 
    } 

    @Override 
    public Class<EventA> handles() { 
     return EventA.class; 
    }  
} 

その後イベントはちょうどマップからハンドラを取り出す処理する必要がある場合

HashMap<Class<?>,Collection<EventHandler<?>> handlers; 

あなたのハンドラを整理するためにマップを使用します。このようなものを使用してください。 Class.equals()とClass.hashCode()がうまくいかない場合は、必要な振る舞いを得るためにラッパーが必要です。

関連する問題