Полиморфно Диспетчерство в Java
В следующем случае я хочу, чтобы EventHandler обрабатывал EventA одним способом, EventB другим способом и любыми другими событиями (EventC, EventD) еще одним способом. EventReceiver получает только ссылку на событие и вызывает EventHandler.handle(). Конечно, эта версия всегда называется EventHandler.handle(событие Event).
Без использования instanceOf существует ли способ полиморфной отправки (возможно, с помощью другого метода в EventHandler или generics) соответствующего метода дескриптора?
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);
}
}
Ответы
Ответ 1
Звучит как пример применения (вариант) шаблона посетителя. (В основных языках OO, таких как С++, С# и 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);
}
}
Ответ 2
Это вариант использования double-dispatch, no (что, как вы знаете, либо называется Visitor)? Я реализую ваш пример только для 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);
}
}
Ответ 3
Java имеет только полиморфную отправку по объекту, на который вызывается метод. Это означает, что единственный способ получить реальный полиморфизм - поместить метод handle()
в сам интерфейс Event
. Я бы сказал, что это лучшее и более эффективное решение OO, поскольку "обработчик", который работает с объектами данных, довольно процедурный.
Любое другое решение (например, карта объектов-обработчиков, связанных с классом) будет более сложным и менее гибким, особенно в отношении наследования.
Ответ 4
Вы можете использовать типы событий Map и map для обработчиков событий.
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);
}
Ответ 5
Я знаю, как вы можете это сделать с некоторой предварительной обработкой. Используйте что-то вроде этого:
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() не работают так, как вам нужно, вам понадобится оболочка, чтобы получить нужное поведение.