Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Factory For A Generic Interface

Tags:

java

factory

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.

like image 429
Prashant Pandey Avatar asked Oct 26 '22 13:10

Prashant Pandey


2 Answers

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) 
like image 157
talex Avatar answered Nov 15 '22 05:11

talex


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.

like image 23
Ole V.V. Avatar answered Nov 15 '22 05:11

Ole V.V.