我正在开发一个小型的web库,我应该将HTTP处理程序方法称为GET、POST、PUT等反射与否。
固定方法
首先,带有if else ...块调用方法的变量在基类中给出,其中它们有一个默认实现,将一个错误返回给客户端。由于对不受支持的方法的请求需要一个包含允许方法的标头,所以我需要仔细查看哪些方法实际上是过度的(顺便说一下,就像Servlet所做的那样)。
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方法之前,基类中的可用方法是固定的。
可变方法
另一个变体是根据其签名(接受HttpServletRequest并返回Response)反射地查找HTTP处理程序方法。这些方法将存储在map中,并根据映射中的键调用reflectivly。
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);
}
}该解决方案的优点是实现简单、灵活,但是由于映射中的搜索和反射方法调用,缺点可能会增加一些运行时开销。类的接口有点“软”(或动态),编译器没有机会检查它。但我不确定这是否会是一个缺点,因为没有其他类应该依赖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方法的固定集合,这里不需要反射。策略模式的方法是干净的,但在我看来却是冗长的。因此,我决定使用固定方法和继承。
发布于 2010-06-25 09:08:54
如何使用接口而不是反射
在这种情况下,不应该使用反射,特别是因为不需要首先使用反射(参见有效的Java第2版,第53项:更喜欢接口而不是反射)。
您应该定义一个java.lang.reflect.Method类型,而不是使用Map<String, Method> handlers,而应该使用一个Map<String, RequestHandler> handlers类型。
看起来会是这样的:
interface RequestHandler {
Response handle(HttpServletRequest req);
}然后,不要反思广益地搜索处理程序,而是使用显式put填充映射(或者使用配置文件等)。然后,您可以更清晰地调用Method.invoke,而不是反射式的RequestHandler.handle。
关于enum键选项
如果您只有几种不同类型的请求方法而没有使其可扩展的计划,则可以使用enum RequestMethod { GET, POST; }。
这允许您声明一个Map<RequestMethod, RequestHandler> handlers;。请记住,enum有一个valueOf(String)方法,您可以使用该方法从名称中获取常量。
论interface对abstract class的使用
在这里,我将再次遵从有效Java 2的乔希·布洛赫的判断,第18项:更喜欢接口而不是抽象类
总结说,
interface通常是定义允许多个实现的类型的最佳方法。这条规则的一个例外是,进化的易用性被认为比灵活性和力量更重要。在这种情况下,您应该使用abstract class来定义类型,但前提是您理解并能够接受这些限制。
这本书更详细地介绍了你正在努力解决的问题。在这种特殊情况下,可能会出现使用abstract class (即“固定方法”方法)的情况,因为有少数几种固定类型的请求方法。
发布于 2010-06-25 09:08:22
我不喜欢在这里使用反射,因为可能的HTTP方法是众所周知的,而且不会在短期内改变。因此,由于额外的复杂性和缺乏运行时检查,您并没有真正获得任何东西。但是,您可以使用一个映射来代替if...elseif,以使您的代码更简洁、更易于扩展。
目前,如果使用"NOSUCHMETHOD“之类的方法名调用反射代码,则会崩溃。
发布于 2010-06-25 11:44:26
一个好的OO设计应该支持可扩展性,但当它是必要的时候。这里有一些关于扩展性意味着什么(对我来说)的一些想法,我和你们分享。
第一级:无扩展性
每件事都是事先知道的,你可以用最简单和最容易理解的方式对它进行编码。
第2级:用钩子封装扩展性
组件封装得很好,但必须能够调整其行为或配置它。然后,组件提供了一个钩子,您可以在其中插入一些东西来改变它的行为。这个钩子可以有很多不同的形式。例如,如果组件提供了一种将策略挂钩到它的方法,或者如果组件内部使用了带有<verb, actionHandler>的映射,但是映射是由另一个组件在外部填充的,等等。
第3级:运行时扩展性
在高级情况下,代码是动态加载的,系统是用DSL表示的自定义规则的“脚本”,等等,这种灵活性要求使用反射,因为需要以动态的方式调用代码。
在您的示例中,HTTP谓词列表是固定的,并且是资源内部的。然后我会选择最简单的方法,使用方法1。在我看来,其他的东西看起来都过于工程化了,当它被证明是合理的时候,我不介意列出一个if-else列表。
https://stackoverflow.com/questions/3116781
复制相似问题