【问题标题】:How to implement Command pattern with PRG in MVC way?如何以 MVC 方式使用 PRG 实现命令模式?
【发布时间】:2015-01-18 15:01:26
【问题描述】:

我正在使用 tomcat、jsp、servlet 和 log4j 开发我的第一个 Web 项目,并且我有一个使用命令设计模式的演示,我对此很感兴趣。我有一个控制器,它接受 doGet 和 doPost 方法,然后处理对 CommandContainer 的请求,CommandContainer 找到合适的命令,执行它,获取资源路径并将客户端转发给它。

public abstract class Command implements Serializable { 
    private static final long serialVersionUID = 8879403039606311780L;
    public abstract String execute(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException;
}

管理命令的CommandContainer:

public class CommandContainer {
    private static final Logger LOG = Logger.getLogger(CommandContainer.class);
    private static Map<String, Command> commands = new TreeMap<String, Command>();
    static {
        // common commands
        commands.put("login", new LoginCommand());
        commands.put("logout", new LogoutCommand());
        commands.put("viewSettings", new ViewSettingsCommand());
        commands.put("noCommand", new NoCommand());

        // client commands
        commands.put("listMenu", new ListMenuCommand());

        // admin commands
        commands.put("listOrders", new ListOrdersCommand());

        LOG.debug("Command container was successfully initialized");
        LOG.trace("Number of commands --> " + commands.size());
    }

public static Command get(String commandName) {
    if (commandName == null || !commands.containsKey(commandName)) {
        LOG.trace("Command not found, name --> " + commandName);
        return commands.get("noCommand"); 
    }

    return commands.get(commandName);
}

我拥有的唯一控制器:

public class Controller extends HttpServlet {

    private static final long serialVersionUID = 2423353715955164816L;

    private static final Logger LOG = Logger.getLogger(Controller.class);

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        process(request, response);
    }

    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        process(request, response);
    }

    private void process(HttpServletRequest request,
            HttpServletResponse response) throws IOException, ServletException {

        LOG.debug("Controller starts");

        // extract command name from the request
        String commandName = request.getParameter("command");
        LOG.trace("Request parameter: command --> " + commandName);

        // obtain command object by its name
        Command command = CommandContainer.get(commandName);
        LOG.trace("Obtained command --> " + command);

        // execute command and get forward address
        String forward = command.execute(request, response);
        LOG.trace("Forward address --> " + forward);

        LOG.debug("Controller finished, now go to forward address --> " + forward);

        // if the forward address is not null go to the address
        if (forward != null) {
            RequestDispatcher disp = request.getRequestDispatcher(forward);
            disp.forward(request, response);
        }
    }

}

我在jsp中使用Controller的方式如下:

...
    <form id="login_form" action="controller" method="post">
                    <input type="hidden" name="command" value="login"/>
...
    </form>

还有web.xml文件:

<servlet>
    <servlet-name>Controller</servlet-name>
    <servlet-class>com.mycompany.web.Controller</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>Controller</servlet-name>
    <url-pattern>/controller</url-pattern>
</servlet-mapping>

我不明白如何使用命令模式实现 Post-Redirect-Get 模式,因为每次请求到达控制器时,它都会使用process() 方法,并且似乎在 JSP 中使用 GET 或 POST 并不重要。然后你会帮助理解使用命令模式的需要吗?如果我将使用多个 servlet,例如 LoginServlet、LogoutServlet、ViewSettingsServlet 而不是一个控制器,那会是一个坏主意,因为我需要将它们硬编码为 jsp 表单作为操作?所有这些问题都让我感到困惑,因为我是初学者,所以请让我理解这一切。

【问题讨论】:

    标签: java jsp servlets command-pattern post-redirect-get


    【解决方案1】:

    嗯,目前,您的命令返回一个字符串:要转发到的 JSP 的名称。如果我理解正确,您还希望能够重定向而不是转发。因此,如果返回的值不是要转发到的视图名称,而是要重定向到的 URL,则需要 tel 告知 servlet。

    有多种方法可以做到这一点。例如,您可以返回一个对象,其中包含要执行的操作类型(FORWARD 或 REDIRECT)以及视图名称或 URL。或者你可以返回一个类似redirect:/foo/bar的字符串,这意味着/foo/bar是一个要重定向到的URL,而不是一个视图名称。

    但是最好的解决方案可能是避免重新发明轮子,并使用现有的 MVC 框架而不是自己实现一个:Spring MVC、Stripes、Struts 等都提供了比你现有的更多的东西,而且在很多方面更好的方法。特别是使用请求参数来选择命令并不是一个很好的选择。使用路径是一个更好的主意。

    您也可以简单地使用多个 servlet,这将比您当前的解决方案更好。但是你会丢失前端控制器,它通常包含所有命令通用的代码:国际化、安全检查等。

    【讨论】:

    • 碰巧我需要国际化我的项目,所以如果有多个控制器是一个好的决定(正如你所说,我失去了前面的一个),我将按照提供的方式使用 JSTL 库回答这个问题:stackoverflow.com/questions/4276061/… ?
    • 失去前端控制器并不是一件好事。这就是为什么我建议使用真正的 MVC 框架而不是自己实现。
    • 您能解释一下为什么它很重要吗?可以给我一个有用的链接
    • 这并不重要。这是因为它有助于在一个中心位置实施常见的事情。见en.wikipedia.org/wiki/Front_Controller_pattern
    • 谢谢你的链接,我也发现这个很有用:oracle.com/technetwork/java/frontcontroller-135648.html。我想我会选择前端控制器,然后尝试告诉他应该使用哪个操作(转发或重定向)。
    猜你喜欢
    • 1970-01-01
    • 2015-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多