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

时间:2010-06-25 09:03:09

标签: 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);
    }

}

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

变量方法

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

}

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

策略模式

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

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方法集,我已经了解到这里不需要反射。策略模式的方法很简洁,但它看起来很冗长。所以我决定采用固定方法和继承。

3 个答案:

答案 0 :(得分:9)

使用接口而不是反射

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

您应该定义Map<String, Method> handlers 类型,而不是使用java.lang.reflect.Method并拥有interface RequestHandler,而应使用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 vs abstract class

在这里,我将再次从 Effective Java 2nd,第18项:首选接口到抽象类推迟Josh Bloch的判断:

  

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

本书已经更详细地介绍了您正在努力解决的问题。在这种特殊情况下,可能是使用abstract class(即“固定方法”方法)的情况,因为那里有少量和固定类型的请求方法。

答案 1 :(得分:7)

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

如果使用类似“NOSUCHMETHOD”的方法名称调用,您的反射代码会崩溃。

答案 2 :(得分:1)

良好的OO设计应该支持可扩展性,但是在需要时应该支持。以下是关于我与您分享的可扩展性(对我而言)的一些想法。

第1级:无可扩展性

事先知道一切,你可以用最简单易懂的方式对其进行编码。

第2级:使用 hooks封装可扩展性

组件封装良好,但必须能够调整其行为或进行配置。该组件提供了一个钩子,您可以在其中插入以改变其行为。钩子可以采用许多不同的形式。例如,组件提供了一种将策略挂钩到其中的方法,或者组件在内部使用了<verb, actionHandler>的映射,但是映射是由另一个组件在外部填充的,等等。

第3级:运行时可扩展性

事情不是先进的,代码是动态加载的,系统是“可编写脚本”的,在DSL中表示自定义规则等。这种程度的灵活性需要使用反射,因为需要在一个代码中调用代码片段。动态的方式。

在您的情况下,HTTP谓词列表是固定的,并且是资源的内部。然后我会选择最简单和最常用的方法1.其他任何东西看起来都像过度工程,我不介意一个小if-else列表,当它被证明是合理的。