如何以 MVC 方式使用 PRG 实现命令模式?
How to implement Command pattern with PRG in MVC way?
我正在使用 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>
我不明白如何使用 Command 模式实现 Post-Redirect-Get 模式,因为每次请求到达控制器时它都使用 process()
方法并且似乎 GET 或POST 用于 JSP。然后你会帮助理解使用命令模式的需要吗?如果我将使用多个 servlet,如 LoginServlet、LogoutServlet、ViewSettingsServlet 而不是一个 Controller,这会不会是个坏主意,因为我需要将它们硬编码为 jsp 形式的操作?所有这些问题让我感到困惑,因为我是初学者,所以请让我明白这一切。
嗯,目前,您的命令 return 是一个字符串:要转发到的 JSP 的名称。如果我理解正确,您还希望能够重定向而不是转发。因此,您需要告诉 servlet,如果 returned 值不是要转发到的视图名称,而是要重定向到的 URL。
有多种方法可以做到这一点。例如,您可以 return 包含要执行的操作类型(FORWARD 或 REDIRECT)和视图名称或 URL 的对象。或者你可以 return 一个像 redirect:/foo/bar 这样的字符串,这意味着 /foo/bar 是一个要重定向到的 URL,而不是一个视图名称。
但最好的解决方案可能是避免重新发明轮子,并使用现有的 MVC 框架而不是自己实现一个框架:Spring MVC、Stripes、Struts 等都提供了更多比你在那里拥有的要好得多。特别是,使用请求参数来选择命令并不是一个很好的选择。使用路径是一个更好的主意。
您也可以简单地使用多个 servlet,这会比您当前的解决方案更好。但是您会丢失前端控制器,它通常包含所有命令通用的代码:国际化、安全检查等。
我正在使用 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>
我不明白如何使用 Command 模式实现 Post-Redirect-Get 模式,因为每次请求到达控制器时它都使用 process()
方法并且似乎 GET 或POST 用于 JSP。然后你会帮助理解使用命令模式的需要吗?如果我将使用多个 servlet,如 LoginServlet、LogoutServlet、ViewSettingsServlet 而不是一个 Controller,这会不会是个坏主意,因为我需要将它们硬编码为 jsp 形式的操作?所有这些问题让我感到困惑,因为我是初学者,所以请让我明白这一切。
嗯,目前,您的命令 return 是一个字符串:要转发到的 JSP 的名称。如果我理解正确,您还希望能够重定向而不是转发。因此,您需要告诉 servlet,如果 returned 值不是要转发到的视图名称,而是要重定向到的 URL。
有多种方法可以做到这一点。例如,您可以 return 包含要执行的操作类型(FORWARD 或 REDIRECT)和视图名称或 URL 的对象。或者你可以 return 一个像 redirect:/foo/bar 这样的字符串,这意味着 /foo/bar 是一个要重定向到的 URL,而不是一个视图名称。
但最好的解决方案可能是避免重新发明轮子,并使用现有的 MVC 框架而不是自己实现一个框架:Spring MVC、Stripes、Struts 等都提供了更多比你在那里拥有的要好得多。特别是,使用请求参数来选择命令并不是一个很好的选择。使用路径是一个更好的主意。
您也可以简单地使用多个 servlet,这会比您当前的解决方案更好。但是您会丢失前端控制器,它通常包含所有命令通用的代码:国际化、安全检查等。