2011-01-19 13 views
7

In seguito, desidero che EventHandler gestisca EventA in un modo, EventB in un altro modo e qualsiasi altro evento (EventC, EventD) in un altro modo. EventReceiver riceve solo un riferimento a un evento e chiama EventHandler.handle(). La versione che viene sempre chiamata, ovviamente, è EventHandler.handle (Evento evento).Invio polymorphic in Java

Senza utilizzare instanceOf, esiste un modo per inviare polimorficamente (magari tramite un altro metodo in EventHandler o generici) al metodo handle appropriato?

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); 
    } 
}  

risposta

10

suona come un caso di applicazione (una variante del) Visitor pattern. (In tradizionali linguaggi OO come C++, C# e Java, metodi sono singola spedizione, cioè può essere solo polimorfa su un tipo alla volta. Visitor permette di implementare doppia spedizione.)

Questo richiede però che sia possibile modificare anche le classi Event e creare una dipendenza da Event s a (un'interfaccia di base di) 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 per il modello di visitatore –

+0

Ho pensato al pattern Visitor e al pattern Vistor aciclico. Sfortunatamente, non posso modificare la classe Event. Inoltre, dovrò aggiungere molti eventi specializzati nel tempo. – user581638

0

È possibile utilizzare una mappa e associare tipi di eventi ai gestori di eventi.

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

Si tratta di un caso d'uso per double-dispatch, no (che, come si può davvero sapere è sia chiamato Visitor)? Io implementare il tuo esempio per Eventa solo

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 ha solo la spedizione polimorfa sull'oggetto un metodo viene richiamato su. Ciò significa che l'unico modo per ottenere un reale polimorfismo è inserire il metodo handle() nell'interfaccia Event. In realtà direi che è la soluzione migliore e più generale OO, dal momento che un "gestore" che opera su oggetti dati è piuttosto procedurale.

Qualsiasi altra soluzione (come una mappa di oggetti del gestore digitati sulla classe) sarà più complessa e meno flessibile, in particolare per quanto riguarda l'ereditarietà.

+0

Sono d'accordo con Michael. Gli eventi, tuttavia, possono essere utilizzati in contesti diversi e dovranno fare le cose in modo diverso in base al contesto. – user581638

0

So come si può fare con un po 'di pre-elaborazione. Usa qualcosa come questo:

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; 
    }  
} 

Quindi utilizzare una mappa per organizzare i gestori

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

Quando un evento deve essere gestita solo recuperare i gestori dalla mappa. Se Class.equals() e Class.hashCode() non funzionano come vuoi, allora avrai bisogno di un wrapper per ottenere il comportamento che desideri.