Design Patterns基于Web的应用程序

我正在设计一个简单的基于Web的应用程序。 我是这个基于Web的领域的新手。我需要关于设计模式的建议,比如Servlet如何分配责任,制定新的Servlet的标准等。

实际上,我的主页上只有很少的实体,并且与其中的每个实体相对应,我们只有几个选项,例如添加,编辑和删除。 早些时候,我使用了一个Servlet作为选项,如Servlet1用于添加entity1,Servlet2用于编辑entity1等,这样我们就得到了大量的servlet。

现在我们正在改变我们的设计。 我的问题是如何正确选择你如何选择servlet的责任。 我们是否应该为每个实体都有一个Servlet来处理它的所有选项并将请求转发到服务层。 还是应该为整个页面设置一个servlet来处理整个页面请求,然后将其转发到相应的服务层? 另外,请求对象是否转发到服务层。


有点体面的Web应用程序由多种设计模式组成。 我只会提及最重要的。


模型视图控制器模式

您想要使用的核心(架构)设计模式是模型 - 视图 - 控制器模式。 控制器将由一个Servlet表示,该Servlet根据请求直接创建/使用特定的模型和视图。 该模型将由Javabean类表示。 这在业务模型中经常可以进一步分解,其中包含包含数据(信息)的动作(行为)和数据模型。 视图将由可通过EL(表达式语言)直接访问(数据)模型的JSP文件表示。

然后,根据行动和事件的处理方式而有所不同。 流行的是:

  • 基于请求(动作)的MVC :这是最简单的实现。 (商业)模型直接与HttpServletRequestHttpServletResponse对象一起工作。 您必须自己收集,转换和验证请求参数(主要是)。 该视图可以用普通香草HTML / CSS / JS表示,并且它不会在请求之间维护状态。 这是Spring MVC,Struts和Stripes的其他作品。

  • 基于组件的MVC :这很难实现。 但是最终你会得到一个更简单的模型和视图,其中所有的“原始”Servlet API都被完全抽象出来。 您不需要自己收集,转换和验证请求参数。 控制器执行此任务并在模型中设置收集,转换和验证的请求参数。 所有你需要做的就是定义直接与模型属性一起工作的动作方法。 视图由JSP标签库或XML元素的“组件”表示,后者又生成HTML / CSS / JS。 会话中维护后续请求的查看状态。 这对服务器端转换,验证和值更改事件特别有用。 这就是其他JSF,Wicket和Play的其中之一! 作品。

  • 作为一个侧面说明,对本土MVC框架的讨论是一个非常好的学习练习,并且只要您为了个人/私人目的而保留它,我就会推荐它。 但是一旦你变得专业,那么强烈建议选择一个现有的框架,而不是重新创建自己的框架。 学习现有的和完善的框架比自己开发和维护一个强大的框架要花费更多的时间。

    在下面的详细解释中,我将自己限制为基于MVC的请求,因为这更容易实现。


    前控制器模式(中介模式)

    首先,控制器部分应该实现前端控制器模式(这是一种特殊的中介模式)。 它应该只包含一个servlet,它提供了所有请求的集中入口点。 它应该根据请求可用的信息创建模型,例如pathinfo或servletpath,方法和/或特定参数。 业务模型在下面的HttpServlet示例中称为Action

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            Action action = ActionFactory.getAction(request);
            String view = action.execute(request, response);
    
            if (view.equals(request.getPathInfo().substring(1)) {
                request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response);
            }
            else {
                response.sendRedirect(view); // We'd like to fire redirect in case of a view change as result of the action (PRG pattern).
            }
        }
        catch (Exception e) {
            throw new ServletException("Executing action failed.", e);
        }
    }
    

    执行该操作应返回一些标识符以查找该视图。 最简单的就是将它用作JSP的文件名。 将此servlet映射到web.xml的特定url-pattern ,例如/pages/**.do或甚至只是*.html

    在前缀模式的情况下,例如/pages/* ,然后您可以调用URL http://example.com/pages/register,http://example.com/pages/login等,并提供/WEB-INF/register.jsp/WEB-INF/login.jsp用适当的GET和POST操作。 然后可以通过request.getPathInfo()像上面的例子一样使用部件registerlogin等。

    当你使用*.do*.html等后缀模式时,你可以调用像http://example.com/register.do,http://example.com/login.do这样的URL,等等,你应该改变这个答案(也是ActionFactory )中的代码示例,以通过request.getServletPath()来提取registerlogin部分。


    战略模式

    Action应遵循战略模式。 它需要被定义为抽象/接口类型,它应该基于抽象方法的传入参数来完成工作(这是与Command模式的区别,其中抽象/接口类型应该基于在实施过程中被传入的参数)。

    public interface Action {
        public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception;
    }
    

    您可能希望使用ActionException类的自定义异常来使Exception更具体。 这只是一个基本的开球的例子,其余的全部取决于你。

    下面是一个LoginAction的例子,它(如其名称所示)登录用户。 User本身又是一个数据模型。 该视图知道User的存在。

    public class LoginAction implements Action {
    
        public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            User user = userDAO.find(username, password);
    
            if (user != null) {
                request.getSession().setAttribute("user", user); // Login user.
                return "home"; // Redirect to home page.
            }
            else {
                request.setAttribute("error", "Unknown username/password. Please retry."); // Store error message in request scope.
                return "login"; // Go back to redisplay login form with error.
            }
        }
    
    }
    

    工厂方法模式

    ActionFactory应该遵循Factory方法模式。 基本上,它应该提供一个创建方法,它返回一个抽象/接口类型的具体实现。 在这种情况下,它应该根据请求提供的信息返回Action接口的实现。 例如,方法和pathinfo(pathinfo是请求URL中的上下文和servlet路径之后的部分,不包括查询字符串)。

    public static Action getAction(HttpServletRequest request) {
        return actions.get(request.getMethod() + request.getPathInfo());
    }
    

    这些actions反过来应该是一些静态/应用程序范围的Map<String, Action> ,它包含所有已知的操作。 这取决于你如何填写这张地图。 硬编码:

    actions.put("POST/register", new RegisterAction());
    actions.put("POST/login", new LoginAction());
    actions.put("GET/logout", new LogoutAction());
    // ...
    

    或者可以根据类路径中的属性/ XML配置文件进行配置:(伪)

    for (Entry entry : configuration) {
        actions.put(entry.getKey(), Class.forName(entry.getValue()).newInstance());
    }
    

    或者动态地基于类路径中的实现特定接口和/或注释的类的扫描:(伪)

    for (ClassFile classFile : classpath) {
        if (classFile.isInstanceOf(Action.class)) {
           actions.put(classFile.getAnnotation("mapping"), classFile.newInstance());
        }
    }
    

    请记住为没有映射的情况创建一个“什么都不做”的Action 。 比如让它直接返回request.getPathInfo().substring(1)


    其他模式

    这些是迄今为止的重要模式。

    为了更进一步,您可以使用Facade模式来创建一个Context类,该类继而包装请求和响应对象,并提供几种委托给请求和响应对象的便利方法,并将其作为参数传递给Action#execute()方法。 这增加了一个额外的抽象层来隐藏原始的Servlet API。 然后,您应该基本上在每个Action实现中都以 import javax.servlet.*声明结束。 用JSF来说,这就是FacesContextExternalContext类正在做的事情。 你可以在这个答案中找到具体的例子。

    然后,对于您希望添加额外抽象层来分割收集请求参数,转换它们,验证它们,更新模型值并执行操作的任务,可以使用State模式。 用JSF来说,这就是LifeCycle所做的。

    然后,对于您希望创建基于组件的视图,您可以使用组合模式,该视图可以与模型一起附加,并且其行为取决于基于请求的生命周期的状态。 在JSF中,这是UIComponent代表的。

    通过这种方式,您可以逐渐向基于组件的框架演进。


    也可以看看:

  • Java核心库中的GoF设计模式示例
  • 请求MVC和组件MVC之间的区别
  • 使用MVC和DAO模式在JSP页面中的HTML中显示JDBC ResultSet
  • JSF MVC框架中的MVC是什么组件?
  • JSF控制器,服务和DAO

  • 在被殴打的MVC模式中,Servlet是“C” - 控制器。

    其主要工作是进行初始请求评估,然后将基于初始评估的处理分派给特定工作人员。 工作人员的职责之一可能是设置一些表示层bean,并将请求转发到JSP页面以呈现HTML。 所以,仅仅因为这个原因,你需要将请求对象传递给服务层。

    不过,我不会开始编写原始的Servlet类。 他们所做的工作是非常可预测的和模板化的,框架非常好。 幸运的是,有许多经过时间考验的候选人(按字母顺序排列):Apache Wicket,Java Server Faces,Spring等等。


    恕我直言,如果您从责任分配的角度来看,那么在web应用的情况下,没有太大的区别。 但是,请保持图层清晰。 在表示层中保留纯粹用于演示目的的任何内容,如特定于Web控件的控件和代码。 只需将业务层中的实体和业务层中的所有功能(如添加,编辑,删除等)保持一致即可。 但是,将它们呈现到浏览器中将在表示层中进行处理。 对于.Net,ASP.NET MVC模式在保持图层分离方面非常出色。 看看MVC模式。

    链接地址: http://www.djcxy.com/p/13353.html

    上一篇: Design Patterns web based applications

    下一篇: Get the companion object instance of a inner modul with the Scala reflection API