Полиморфно Диспетчерство в 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() не работают так, как вам нужно, вам понадобится оболочка, чтобы получить нужное поведение.