反射调用方法或使用带继承的固定方法?

dea*_*mon 8 java oop reflection inheritance

我正在研究一个小型的Web库,并且想知道我应该反复调用GET,POST,PUT等HTTP处理程序方法.

固定方法

首先是if else ...在基类中给出块调用方法的变体,它们具有向客户端返回错误的默认实现.由于对不受支持的方法的请求需要带有允许方法的标头,我需要反思地查找哪些方法被覆盖(顺便说一下,Servlet API就是这样).

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

}
Run Code Online (Sandbox Code Playgroud)

此解决方案的缺点是灵活性降低,因为可用的方法在基类中handle得到修复,直到方法在子类中重新实现为止.

变量方法

另一种变体是根据其签名(获取HttpServletRequest和返回Response)反射查找HTTP处理程序方法.这些方法将存储在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);
    }

}
Run Code Online (Sandbox Code Playgroud)

此解决方案的优点是简单的实现和灵活性,但由于在地图中搜索和反射方法调用,缺点可能是更多的运行时开销.并且类的接口有点"软"(或动态),编译器没有机会检查它.但我不确定这是不是一个缺点,因为没有其他类应该依赖HTTP处理程序方法,它们是外部Web接口和Java系统的边界.

战略模式

第三种选择和最干净的OOP将是"polygenelubricants"推荐的策略模式.它看起来像这样:

class MyResource extends Resource {

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

}
Run Code Online (Sandbox Code Playgroud)

它是干净的OOP,但代码实际上是丑陋和冗长的.虽然Scala的工具支持仍然很差,但我更喜欢Scala和闭包.将此与具有继承和固定方法的解决方案进行比较:

class MyResource extends Resource {

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

}
Run Code Online (Sandbox Code Playgroud)

你更喜欢什么?为什么?其他想法?

我已经知道由于固定的HTTP方法集,这里不需要反射.策略模式的方法很简洁,但它看起来很冗长.所以我决定采用固定的方法和继承.

pol*_*nts 9

使用接口而不是反射

在这种情况下不应该使用反射,特别是因为没有必要开始使用(参见Effective Java 2nd Edition,Item 53:Prefer接口到反射).

您应该定义一个类型,而不是使用java.lang.reflect.Method和拥有一个类型.Map<String, Method> handlersinterface RequestHandler Map<String, RequestHandler> handlers

它看起来像这样:

interface RequestHandler {
   Response handle(HttpServletRequest req);
}
Run Code Online (Sandbox Code Playgroud)

然后,不是反复搜索处理程序,而是put使用显式填充映射(或者使用配置文件等).然后,而不是反思Method.invoke,你可以更干净地打电话RequestHandler.handle.


enum键选项

如果您只有几种不同类型的请求方法而无法使其可扩展,那么您可以拥有一个enum RequestMethod { GET, POST; }.

这允许你声明一个Map<RequestMethod, RequestHandler> handlers;.请记住,enum有一个valueOf(String)方法可以用来从名称中获取常量.


关于使用interfacevs.abstract class

在这里,我将再次遵循Josh Bloch对Effective Java 2nd,Item 18:Prefer接口到抽象类的判断:

总而言之,interface通常是定义允许多个实现的类型的最佳方式.这一规则的一个例外是,易于进化被认为比灵活性和权力更重要.在这种情况下,您应该使用a abstract class来定义类型,但前提是您理解并且可以接受这些限制.

你正在努力解决的问题已被本书详尽地详述.在这种特殊情况下,可能存在使用abstract class(即"固定方法"方法)的情况,因为那里有少量和固定类型的请求方法.


Pét*_*rök 7

我不希望在这里使用反射,因为可能的HTTP方法是众所周知的,并且不会很快改变.所以你并没有真正获得额外的复杂性和缺乏运行时检查.而不是if...elseif你可以使用地图,以使你的代码更清洁,更容易扩展.

目前,如果使用类似"NOSUCHMETHOD"的方法名称调用,您的反射代码将崩溃.