用Java进行多态调度

时间:2011-01-19 15:50:01

标签: java double-dispatch

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

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

5 个答案:

答案 0 :(得分:14)

听起来像应用Visitor pattern的变体一样。 (在主流的OO语言中,如C ++,C#和Java,方法是单一调度,即一次只能在一种类型上进行多态。访问者允许实现双重调度。)

然而,这要求您也能够修改Event类,并创建从EventEventHandler的基本接口的依赖关系。

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 :(得分: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);
    }
}    

答案 2 :(得分:1)

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

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

答案 3 :(得分: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);
}

答案 4 :(得分: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()没有按你想要的方式工作,那么你需要一个包装器来获得你想要的行为。