2011-01-19 18 views
7

W dalszej części chcę, aby EventHandler obsługiwał EventA w jedną stronę, EventB w inny sposób i wszelkie inne zdarzenia (EventC, EventD) w jeszcze inny sposób. EventReceiver odbiera tylko odwołanie do zdarzenia i wywołuje funkcję EventHandler.handle(). Wersja, która zawsze jest wywoływana, oczywiście jest EventHandler.handle (zdarzenie Event).Polimorficzne wysyłanie w Javie

Bez użycia obiektu instanceOf, czy istnieje sposób na polimorficzną wysyłkę (być może za pośrednictwem innej metody w EventHandler lub generics) do odpowiedniej metody obsługi?

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

Odpowiedz

10

Brzmi przypadku nakładania (wariant) w Visitor pattern. (W głównych językach OO takich jak C++, C# i Java, metody są pojedyncza wysyłka, czyli może być tylko polimorficzny na jednym typie naraz. Visitor pozwala wdrożyć podwójne wysyłkę.)

Wymaga to jednak można również modyfikować klasy Event i tworzy zależność od Event s do (podstawowego interfejsu) EventHandler.

+1

+1 za wzór gościa –

+0

Pomyślałem o wzorze Odwiedzającego i acyklicznego wzoru Vistora. Niestety nie mogę zmodyfikować klasy Event. Ponadto będę musiał z czasem dodać wiele specjalistycznych wydarzeń. – user581638

0

Do obsługi zdarzeń można użyć typów zdarzeń Mapa i Mapa.

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

Jest to przypadek użycia dla double-dispatch, no (który jako jeden może rzeczywiście wiedzieć, albo nazywa Visitor)? będę realizować swoje przykład dla Eventa tylko

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 ma tylko polimorficzny wysyłkę na obiekcie metoda wywoływana jest. Oznacza to, że jedynym sposobem na uzyskanie prawdziwego polimorfizmu jest umieszczenie metody handle() w samym interfejsie handle(). Powiedziałbym, że jest to ogólnie lepsze i więcej rozwiązanie OO, ponieważ "handler", który działa na obiektach danych, jest raczej proceduralny.

Każde inne rozwiązanie (takie jak mapa obiektów obsługi z kluczem klasy) będzie bardziej złożone i mniej elastyczne, szczególnie w przypadku dziedziczenia.

+0

Zgadzam się z Michaelem. Wydarzenia mogą być jednak wykorzystywane w różnych kontekstach i będą musiały robić różne rzeczy w zależności od kontekstu. – user581638

0

Wiem, jak to zrobić z pewnym wstępnym przetwarzaniem. Używać coś takiego:

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

Następnie za pomocą mapy, aby uporządkować ładowarki

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

Kiedy zdarzenie musi być obsługiwane tylko odzyskać teleskopowe z mapy. Jeśli Class.equals() i Class.hashCode() nie działa, jak chcesz, to potrzebujesz wrapper, aby uzyskać pożądane zachowanie.

Powiązane problemy