我正在开发一个小型的Web库,想知道是否应该以反射方式调用GET,POST,PUT等的HTTP处理程序方法。
固定方法
首先,带有if else ...
块的变体调用基类中给出的方法,该方法具有默认实现,将错误返回给客户端。由于对不支持方法的请求需要带有允许方法的 header ,因此我需要反射(reflection)性地查找被强制覆盖的方法(顺便说一下,就像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);
}
}
该解决方案的缺点是灵活性降低,因为在基类中固定了可用方法,直到在子类中重新实现
handle
方法为止。可变方法
另一个变体是根据HTTP处理程序方法的签名进行反射性查找(采用
HttpServletRequest
并返回Response
)。这些方法将存储在Map中,并根据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);
}
}
该解决方案的优点是简单的实现和灵活性,但缺点是由于在映射中进行搜索和反射性方法调用而可能会增加运行时开销。该类的接口(interface)有些“软”(或动态),并且编译器没有机会对其进行检查。但是我不确定这是否是不利的,因为其他任何类都不应该依赖HTTP处理程序方法,它们是外部Web界面和Java系统的边界。
策略模式
第三种选择和最清洁的OOP将是“多基因润滑剂”建议的策略模式。它看起来像这样:
class MyResource extends Resource {
register("GET",
new RequestHandler{
@Override Response handle(HttpServletRequest request) {
new Response(OK);
}
}
);
}
它是干净的OOP,但是代码非常丑陋且冗长。即使对Scala的工具支持仍然很差,我还是更喜欢在这里使用闭包的Scala。将此与带有继承和固定方法的解决方案进行比较:
class MyResource extends Resource {
@Override Response get(HttpServletRequest request) {
return new Resonse(OK);
}
}
您想要什么,为什么?还有其他想法吗?
解决方案
我知道由于固定的HTTP方法集,这里不需要反射。采用策略模式的方法是干净的,但对我来说似乎很冗长。因此,我决定使用固定方法和继承。
最佳答案
关于使用接口(interface)而不是反射
在这种情况下,不应该使用反射,尤其是因为它没有必要从头开始(请参阅有效的Java 2nd Edition,项目53:首选接口(interface)而不是反射)。
不应使用 java.lang.reflect.Method
并使用Map<String, Method> handlers
,而应定义interface RequestHandler
类型,并改为使用Map<String, RequestHandler> handlers
。
它看起来像这样:
interface RequestHandler {
Response handle(HttpServletRequest req);
}
然后,与其使用反射式搜索处理程序,不如使用显式的
put
填充 map (或使用配置文件等)。然后,您可以更干净地调用Method.invoke
,而不是反射地RequestHandler.handle
。在
enum
键选项上如果您只有几种不同类型的请求方法,而又没有使其可扩展的计划,则可以使用
enum RequestMethod { GET, POST; }
。这使您可以声明
Map<RequestMethod, RequestHandler> handlers;
。请记住,enum
具有valueOf(String)
方法,您可以使用该方法从名称中获取常量。关于
interface
和abstract class
的使用在这里,我将再次引用Josh Bloch从有效Java 2nd,第18项:对抽象类的首选接口(interface)的判断:
您所苦苦挣扎的问题已在本书中详细介绍了。在这种特定情况下,由于那里的请求方法很少且是固定类型,因此可能存在使用
abstract class
(即“固定方法”方法)的情况。