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方法集,这里不需要反射.策略模式的方法很简洁,但它看起来很冗长.所以我决定采用固定的方法和继承.
在这种情况下不应该使用反射,特别是因为没有必要开始使用(参见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通常是定义允许多个实现的类型的最佳方式.这一规则的一个例外是,易于进化被认为比灵活性和权力更重要.在这种情况下,您应该使用aabstract class来定义类型,但前提是您理解并且可以接受这些限制.
你正在努力解决的问题已被本书详尽地详述.在这种特殊情况下,可能存在使用abstract class(即"固定方法"方法)的情况,因为那里有少量和固定类型的请求方法.
我不希望在这里使用反射,因为可能的HTTP方法是众所周知的,并且不会很快改变.所以你并没有真正获得额外的复杂性和缺乏运行时检查.而不是if...elseif你可以使用地图,以使你的代码更清洁,更容易扩展.
目前,如果使用类似"NOSUCHMETHOD"的方法名称调用,您的反射代码将崩溃.