Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling methods reflectivly or using fixed methods with inheritance?

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.

like image 667
deamon Avatar asked Jun 25 '10 09:06

deamon


1 Answers

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.

like image 85
polygenelubricants Avatar answered Sep 20 '22 07:09

polygenelubricants