views:

101

answers:

3

I'm working on a tiny web library and wonder wheter I should call the HTTP handler methods for GET, POST, PUT etc. reflectivly or not.

Fixed Methods

First the variant with an if else ... block calling methods given in the base class where they have a default implementation returning an error to the client. Since a request to an unsupported method needs a header with the allowed methods, I need to look up reflectivly what methods are actully overriden (like the Servlet API does, by the way).

public abstract class Resource {

    public Response handle(HttpServletRequest request) {
        String action = request.getMethod();
        if(action.equals("GET"))
            return get(request);
        else if(action.equals("POST"))
            return post(request);
        ...
    }

    protected Response get(HttpServletRequest request) {
        return new Response(METHOD_NOT_ALLOWED);
    }

    protected Response post(HttpServletRequest request) {
        return new Response(METHOD_NOT_ALLOWED);
    }

}

The disadvantage of this solution is the reduced flexible, since the usable methods are fixed in the base class until the handle methods gets reimplemented in a subclass.

Variable Methods

The other variant is to look up HTTP handler methods reflectivly depending on their signature (take HttpServletRequest and return Response). These methods would be stored in a Map and called reflectivly depending on the key in the map.

public abstract class Resource {

    private Map<String, Method> handlers;

    public Resource() {
        handlers = findHttpHandlerMethodsReflectivly();
    }

    public Response handle(HttpServletRequest request) {
        String action = request.getMethod();
        Method handler = handlers.get(action);
        return (Response)handler.invoke(this, request);
    }

}

The advantage of this solution is the simple implementation and flexibilty, but the disadvantages are perhaps a bit more runtime overhead due to the search in the map and the reflective method call. And the interface of the class is somewhat "soft" (or dynamic) and the compiler has no chance to check it. But I'm not sure if this would be a disadvantage since no other classes should rely on the HTTP handler methods, they are the external web interface and the border of the Java system.

Strategy Pattern

The third option and the cleanest OOP would be the strategy pattern as recommend by "polygenelubricants". It would look like this:

class MyResource extends Resource {

    register("GET", 
        new RequestHandler{
            @Override Response handle(HttpServletRequest request) {
                new Response(OK);
            }
        }
    );

}

It is clean OOP but the code is realy ugly and verbose. I would prefer Scala with closures here even though the tool support for Scala is still poor. Compare this to the solution with inheritance and fixed methods:

class MyResource extends Resource {

    @Override Response get(HttpServletRequest request) {
        return new Resonse(OK);
    }

}

What would you prefer and why? Other ideas?

Solution

I've learned that reflection is not needed here due to the fixed set of HTTP methods. The approach with the strategy pattern is clean, but it looks to verbose to me. So I decided to go with the fixed methods and inheritance.

+7  A: 

I would prefer not using reflection here, as the possible HTTP methods are well known and are not going to change anytime soon. So you are not really gaining anything for the additional complexity and lack of runtime checks. Instead of if...elseifs you could use a map though, to make your code cleaner and easily extensible.

Currently your reflective code would crash if called with a method name like "NOSUCHMETHOD".

Péter Török
And what should be the value of a map entry?
deamon
@deamon, it could map from Strings to some strategy classes (or instances if they are stateless). The strategy in this case could easily be implemented within an enum. See an example in this earlier answer of mine: http://stackoverflow.com/questions/2913495/refactoring-if-else-logic/2913518#2913518 . Admittedly, it is a borderline case - with only 3-4 different methods (and no prospect of changes) the `if...elseif` solution is actually simpler.
Péter Török
+9  A: 

On using interfaces instead of reflection

Reflection should not be used in this case, especially since it's not necessary to begin with (see Effective Java 2nd Edition, Item 53: Prefer interfaces to reflection).

Instead of using java.lang.reflect.Method and having a Map<String, Method> handlers, you should define an interface RequestHandler type, and have a Map<String, RequestHandler> handlers instead.

It would look something like this:

interface RequestHandler {
   Response handle(HttpServletRequest req);
}

Then instead of searching for handlers reflectively, populate the map with an explicit put (or perhaps use a configuration file, etc). Then, instead of reflectively Method.invoke, you can more cleanly just call RequestHandler.handle.


On enum keys option

If you only have a few different types of request methods with no plan of making it extensible, you can have an enum RequestMethod { GET, POST; }.

This allows you to declare a Map<RequestMethod, RequestHandler> handlers;. Remember that enum has a valueOf(String) method that you can use to get the constant from the name.


On using interface vs abstract class

Here I will again defer to the judgement of Josh Bloch from Effective Java 2nd, Item 18: Prefer interfaces to abstract classes:

To summarize, an interface is generally the best way to define a type that permits multiple implementation. An exception to this rule is the case where ease of evolution is deemed more important than flexibility and power. Under these circumstances, you should use an abstract class to define the type, but only if you understand and can accept the limitations.

The issues that you're struggling with have been thoroughly covered by the book in much greater detail. In this particular case, there may be a case for using an abstract class (i.e. the "fixed method" approach) because of the few and fixed types of request methods out there.

polygenelubricants
I know the strategy pattern, but I think it would lead to bloated code here, since I'd need to implement a class for each http handler method. But it would be clean in terms of OOP.
deamon
@deamon: Java is verbose anyway, but good OOP design should be a high priority in these kinds of decisions.
polygenelubricants
+1  A: 

A good OO design should support extensibility, but one when it is required. Here are a few thoughts about what extensibility means (for me) which I share with you.

Level 1: no extensibility

Everything is known in advance, you can code it the way that makes it the most simple and understandable.

Level 2: encapsulated extensibility with hooks

A component is well encapsulated, but it must be possible to adapt its behavior or configure it. The component provides then a hook where you can plug things to change its behavior. The hook can take a lot of different forms though. This is the case for instance is a component provide a way to hook a strategy into it, or if a map with <verb, actionHandler> was used by the component internally, but the map is filled externally by another component, etc.

Level 3: run-time extensibility

Things are not know in advanced, code is loaded dynamically, the system is "scriptable" with custom rules expressed in a DSL, etc. This degree of flexibility requires the usage of reflection because piece of code need to be invoked in a dynamic way.

In your case, the list of HTTP verbs is fixed, and internal to the resource. I would then go for the simplest and use approach 1. Everything else looks like over-engineering to me and I don't mind a little if-else list when it's justified.

ewernli