15

在下文中,我希望 EventHandler 以一种方式处理 EventA,以另一种方式处理 EventB,并以另一种方式处理任何其他事件(EventC、EventD)。EventReceiver 仅接收对 Event 的引用并调用 EventHandler.handle()。当然,总是被调用的版本是 EventHandler.handle(Event event)。

在不使用 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);
    }
}    
4

5 回答 5

14

听起来像是应用访问者模式(的变体)的一个案例。(在C++、C#、Java等主流OO语言中,方法是单调度的,即一次只能多态一种类型。访问者允许实现双调度。)

但是,这要求您也能够修改Event类,并创建从Events 到 (a base interface of)的依赖关系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);
    }
}
于 2011-01-19T15:52:58.850 回答
2

Java 仅在调用方法的对象上具有多态调度。这意味着,获得真正多态性的唯一方法是将handle()方法放入Event接口本身。我实际上会说这是整体上更好、更面向对象的解决方案,因为对数据对象进行操作的“处理程序”是相当程序化的。

任何其他解决方案(例如在类上键入的处理程序对象的映射)都将更加复杂且不太灵活,尤其是在继承方面。

于 2011-01-19T15:58:20.763 回答
2

这是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);
    }
}    
于 2011-01-19T15:56:40.500 回答
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() 不能按照您的意愿工作,那么您将需要一个包装器来获得您想要的行为。

于 2012-12-17T19:34:52.307 回答
0

您可以使用 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);
}
于 2011-01-19T15:55:04.120 回答