开发者

Polymorphically Dispatching in Java

In the following, I want EventHandler to handle EventA one way, EventB another way, and any other Events (EventC, EventD) yet another way. EventReceiver receives only a reference to an Event and calls EventHandler.handle(). The version that always gets called, of course, is EventHandler.handle(Event event).

Without using instanceOf, is there a way to polymorphically dispatch (perhaps via another method in EventHandler or generics) to the appropriate开发者_JAVA百科 handle method?

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


Sounds like a case for applying (a variant of) the Visitor pattern. (In mainstream OO languages such as C++, C# and Java, methods are single dispatch, i.e. can only be polymorphic on one type at a time. Visitor allows one to implement double dispatch.)

This however requires that you be able to modify the Event classes as well, and creates a dependency from Events to (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);
    }
}


This is a use case for double-dispatch, no (which as one may indeed know is either called Visitor) ? I'll implement your example for EventA only

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


Java only has polymorphic dispatch on the object a method is invoked on. That means, the only way to get real polymorphism is to put the handle() method into the Event interface itself. I'd actually say that is the overall better and more OO solution, since a "handler" that operates on data objects is rather procedural.

Any other solution (like a map of handler objects keyed on the class) is going to be more complex and less flexible, especially concerning inheritance.


You could use a Map and map event types to event handlers.

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


I know how you can do it with some pre-processing. Use something like this:

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

Then use a map to organize your handlers

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

When an event needs to be handled just retrieve the handlers from the map. If Class.equals() and Class.hashCode() doesn't work how you want then you'll need a wrapper to get the behavior you want.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜