I have an interface:
public interface Handler<E extends Event> {
void handle(E event);
}
And two implementations of it:
public class SignupEventHandler implements Handler<SignupEvent> {
@Override
public void handle(SignupEvent event) {}
}
and
public class SignoutEventHandler implements Handler<SignoutEvent> {
@Override
public void handle(SignoutEvent event) {}
}
Note that Event
in itself is an interface, that is implemented by both SignupEvent
and SignoutEvent
.
I am wondering what's the correct way to implement a factory for Handler
.
public class HandlerFactory {
public static Handler<? extends Event> getHandler(Event event) {
if(event.type().equals("SignupEvent")) {
return new SignupEventHandler();
} else if(event.type().equals("SignoutEvent")) {
return new SignoutEventHandler();
} else {
throw new IllegalArguementException("Unrecognised event type");
}
}
}
The problem I am facing is that I can't do HandlerFactory.getHandler(event).handle(event)
as it wants a capture of ? of Event
.
Event event = getDeserialisedEvent(message, Event.class);
Handler<? extends Event> handler = HandlerFactory.getHandler(event);
handler.handle(event); //can't do this
Is there a clean way to accomplish this? One way I found was:
public class HandlerFactory {
public static <E extends Event> Handler<E> getHandler(E event) {
if(event.type().equals("SignupEvent")) {
return (Handler<E>) new SignupEventHandler(); //unchecked cast, but it's guaranteed to be type-safe
} else if(event.type().equals("SignoutEvent")) {
return (Handler<E>) new SignoutEventHandler(); //unchecked cast, but it's guaranteed to be type-safe
} else {
throw new IllegalArguementException("Unrecognised event type");
}
}
}
This approach works, but I wonder if there is a cleaner way to do this.
There are slightly different ways, but they are all basically same. All of them use cast in one way or another.
For example you can use raw types:
public static Handler getHandler(Event event)
You may solve it nicely if instead of a factory you have a class that delegates to the right handler.
public class EventHandlerFacade {
public void handle(Event event) {
if (event instanceof SignupEvent) {
new SignupEventHandler().handle((SignupEvent) event);
}
else if (event instanceof SignoutEvent) {
new SignoutEventHandler().handle((SignoutEvent) event);
}
else {
System.out.println("Unhandled event of class " + event.getClass());
}
}
}
In this code Java can see that the casts are safe. It will work no matter if the facade creates the handler each time it needs one (as in the code), or it keeps handlers around and reuses them.
You may also want to live with what you have. Generics are for compile-time type checking. When you don’t know the actual (runtime) type of event
at compile time, compile-time type checking doesn’t make that much sense. So you may opt to dispense with it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With