Servlet的模板模式

一、什么是Servlet的模板模式?

​ 我们先来看一下模板模式的定义:在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。(摘自菜鸟教程,原文链接:https://www.runoob.com/design-pattern/template-pattern.html)

​ 这是比较官方的定义,感觉很抽象,看不太懂是不??这很正常,其实我刚开始也看不太懂,先别着急,先让我慢慢分析,到后面你就会直呼好家伙。

二、为什么要使用Servlet的模板模式?

​ 要理解使用模板模式的目的,我们先来看一下Servlet家族的继承结构:
在这里插入图片描述
​ 最底层的MyServlet是我自己定义的一个Servlet(最顶层的我们只看Servlet就行了,另外两个别看哈)。其实,我们都知道,Servlet规范的要求就是要实现Servlet接口,也就是说,凡是实现了Servlet接口的类都可以作为一个处理类而存在(处理来自前端的请求),但事实上,我们处理Servlet请求的时候,都是直接继承HttpServlet的,那么,我们可以直接实现Servlet吗??

​ 答案是可以的,其实,中间的两个类是为了能够简化实现Servlet的难度而存在的。如果我们直接实现Servlet接口,那么我们需要实现这个接口内的所有方法,

在这里插入图片描述

​ 但实际上,真正用来处理前端请求的Servlet就service一个方法,以此类推,如果我们直接实现GenericServlet接口,那么我们需要不得不实现一个service方法,但这个service方法是可以处理所有的请求的,它不需要区分请求是get还是post,这样就造成了一个麻烦,如果我们来自前端的请求是都是get或者都是post的话,那完全没有问题,我们直接使用service方法处理请求就可以了,但问题是我们实际开发中几乎不存在这样的需求。因此,后面又引入了一个HttpServlet类,这个类完全帮助我们适配了所有的方法,我们只需要按照需求重写里面的方法就可以了,这样就十分的灵活了。

​ OK,理解了这一层,我们来看一个需求,比如说,你现在要开发一个学生信息管理系统,假设里面有三个模块,分别是教师模块,学生模块,课程模块,我们先就最简单的需求来说,每个模块都需要做相应的增删改查吧,那么一共就是需要12个Servlet来处理。

​ 好,你可能会觉得,也就谁12个Servlet而已,这很简单啊,但你想过没有,实际的项目开发过程中,不可能只有这么几个模块,而且每个模块也不可能就只有增删改查四个需求,这个时候,如果每个需求都对应一个Servlet的话,那如果项目有几百个需求,那你就需要写几百个Servlet,这就不仅十分的繁琐,而且后期还极难维护。那么,怎么办呢?其实Servlet的模板设计模式就是解决这个问题的。

三、如何实现Servlet的模板模式?

​ 我们以上述的学生信息管理系统为例,事实上,每个模块只需要一个Servlet即可,**了每个模块的Servlet定义程序执行的流程或者算法的骨架搭建出来,里面具体的实现方式交给方法去做。**我们来看具体是怎么实现的(以学生模块为例,其他两个模块类似):

【1】先定义一个前端页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>$Title$</title>
    </head>
    <body>
        <h2>学生信息管理系统</h2>
        <br><br>
        <a href="student/add.do">添加学生</a><br>
        <a href="student/delete.do">删除学生</a><br>
        <a href="student/update.do">修改学生</a><br>
        <a href="student/select.do">查询学生</a>
    </body>
</html>

【2】定义一个处理该请求的StudentServlet,并将其注册到web.xml文件中

<servlet>
    <servlet-name>StudentServlet</servlet-name>
    <servlet-class>com.beim.controller.StudentServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>StudentServlet</servlet-name>
    <url-pattern>/student/add.do</url-pattern>
    <url-pattern>/student/delete.do</url-pattern>
    <url-pattern>/student/update.do</url-pattern>
    <url-pattern>/student/select.do</url-pattern>
</servlet-mapping>
public class StudentServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.service(req, resp);
    }
}

​ 好,你可能会问,这里为啥要使用service来处理请求呢?由于我们在web.xml文件中注册的时候,使用的有四个,也就是说,这个StudentServlet是用来处理这四个请求的,它其实就相当于一个模板,那么处理四个请求,我们肯定不能再使用doGet、doPost方法来处理了,因为我们无法保证四个方法处理的都是同一种类型的请求,所以这里我们选择重写了万能的处理请求service()方法。

​ 那么问题又来了,我们怎么知道来自前端的请求是增删改查中的哪一个呢?其实service方法的req请求对象里面已经封装好了很多的方法,可以供我们获取到来自请求的地址,我们只需要通过对地址进行判断即可是哪一个方法了。

// 获取请求的相对地址
String servletPath = req.getServletPath();

​ 其实这个相对地址就是标签的值,那么我们只需要判断servletPath的值与这个四个地址值即可:

public class StudentServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取请求的相对地址
        String servletPath = req.getServletPath();
        if ("/student/add.do".equals(servletPath)){
            // 添加
            add(req, resp);
        }else if ("/student/delete.do".equals(servletPath)){
            // 删除
            delete(req,resp);
        }else if ("/student/update.do".equals(servletPath)){
            // 修改
            update(req,resp);
        }else if ("/student/select.do".equals(servletPath)){
            // 查询
            select(req,resp);
        }
    }

    public void add(HttpServletRequest req, HttpServletResponse resp){
        // 执行添加的业务逻辑
    }

    public void delete(HttpServletRequest req, HttpServletResponse resp){
        // 执行删除的业务逻辑
    }

    public void update(HttpServletRequest req, HttpServletResponse resp){
        // 执行更新的业务逻辑
    }

    public void select(HttpServletRequest req, HttpServletResponse resp){
        // 执行查询的业务逻辑
    }
}

​ 这里我提一下,为啥要单独分离出四个方法呢?这个其实考虑的有两点。第一就是代码的可读性,因为真实的开发可能业务逻辑十分的复杂,如果你全部写在if判断语句里面,那if括号会十分的冗长,不利于阅读,第二就是代码的复用性,这也是考虑到如果四各业务全部写到if语句中,如果四个业务逻辑有相同的代码部分,那么我们就需要写4遍,这也极大的增加了开发的难度

逻辑十分的复杂,如果你全部写在if判断语句里面,那if括号会十分的冗长,不利于阅读**,第二就是代码的复用性,这也是考虑到如果四各业务全部写到if语句中,如果四个业务逻辑有相同的代码部分,那么我们就需要写4遍,这也极大的增加了开发的难度

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值