3、Template Method 模板方法 行为型设计模式

1、了解模板方法

1.1 模式定义

定义一个操作算法中的框架,而将这些步骤延迟加载到子类中。

它的本质就是固定算法框架。

1.2 解决何种问题

让父类控制子类方法的调用顺序

模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

1.3 模式好处

开发人员在开发时,只需要考虑方法的实现。不需要考虑方法在何种情况下被调用。实现代码复用。

1.4 模式适合场景

  • 一次性实现一个算法的不变部分,并将可变的行为留给子类来实现。

  • 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。

  • 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。

2、示例程序

例子说明:

 小明来买房.业务员小二接待小明.小二带领小明去看房,小明觉得还不错.留下了联系方式,就离开了
 
 小明来买房.业务经理小强接待小明.小二带领小明去看房,提出了一些建议和购房优惠,小明觉得优惠还不错.留下了联系方式并且交了押金,就离开了
 
 经理还是挺会套路的,哈哈哈哈

2.1、抽象类,定义模板

定义一个AbstractDisplay抽象类

 package cn.design.template;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-15 17:09
  * @Description 抽象类, 定义模板方法
  */
 public abstract class AbstractDisplay {
     public abstract void join();
 
     public abstract void execute();
 
     public abstract void exit();
 
     /**
      * 模板方法
      */
     public final void display() {
         join();
         execute();
         exit();
    }
 
 }
 

2.2、业务员小二

定义一个SalesmanDisplay继承AbstractDisplay

 package cn.design.template;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-15 17:14
  * @Description 业务员 小二
  */
 public class SalesmanDisplay extends AbstractDisplay {
     /**
      * 小明
      */
     String name;
 
     public SalesmanDisplay(String name) {
         this.name = name;
    }
 
     @Override
     public void join() {
         System.out.println("------- start -------");
         System.out.println("-- 我是" + this.name + " 来看看房--");
         System.out.println("-- 你好 ,我是 业务员小二 , 走 我带你来看房");
    }
 
     @Override
     public void execute() {
         System.out.println("----------- look --------");
         System.out.println(" 这个房还是挺大的,而且还是新房,精装修的 ");
    }
 
     @Override
     public void exit() {
         System.out.println("----------- exit ---------");
         System.out.println("今天看房效果还不错,双方留下了联系方式");
    }
 }
 

2.3、经理小强

定义ManagerDisplay继承AbstractDisplay

 package cn.design.template;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-15 17:14
  * @Description 经理 小强 的做法
  */
 public class ManagerDisplay extends AbstractDisplay {
     /**
      * 小明
      */
     String name;
 
     public ManagerDisplay(String name) {
         this.name = name;
    }
 
     @Override
     public void join() {
         System.out.println("------- start -------");
         System.out.println("-- 我是" + this.name + " 来看看房--");
         System.out.println("-- 你好 ,我是 经理小强 ,我带你来看房 而且还有一些优惠政策");
    }
 
     @Override
     public void execute() {
         System.out.println("----------- look --------");
         System.out.println(" 这个房区是属于学区房, 最近我们搞活动,你提前交一些定金,回头会有打98折的活动");
         System.out.println(" 我都会为你全权负责的, 这个你就放心好了,放心买 ");
    }
 
     @Override
     public void exit() {
         System.out.println("----------- exit ---------");
         System.out.println("小明愚蠢的交了押金,双方留下了联系方式,后来小明不要买了,押金没了");
    }
 }
 

2.4、Main测试类

 package cn.design.template;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-15 16:09
  * @Description TODO
  */
 public class Main {
     public static void main(String[] args) {
         AbstractDisplay a1 = new SalesmanDisplay("小明啊");
         a1.display();
         System.out.println();
         AbstractDisplay a2 = new ManagerDisplay("ming");
         a2.display();
    }
 }
 

运行结果如下所示:

 ------- start -------
 -- 我是小明啊 来看看房--
 -- 你好 ,我是 业务员小二 , 走 我带你来看房
 ----------- look --------
  这个房还是挺大的,而且还是新房,精装修的
 -----------  exit  ---------
 今天看房效果还不错,双方留下了联系方式
 
 ------- start -------
 -- 我是ming 来看看房--
 -- 你好 ,我是 经理小强 ,我带你来看房 而且还有一些优惠政策
 ----------- look --------
  这个房区是属于学区房, 最近我们搞活动,你提前交一些定金,回头会有打98折的活动
  我都会为你全权负责的, 这个你就放心好了,放心买
 -----------  exit  ---------
 小明愚蠢的交了押金,双方留下了联系方式,后来小明不要买了,押金没了

3、模式结构

在Template Method模工中有以下登场用色。

◆ AbstractClass (抽象类)

AbstractClass角色不仅负责实现模板方法,还负责声明在模板方法中所使用到的抽象方法。这些抽象方法由子类ConcreteClass角色负责实现。在示例程序中,由AbstractDisplay类扮演此角色。

◆ ConcreteClass (具体类)

该角色负责具体实现AbstractClass角色中定义的抽象方法。这里实现的方法将会在AbstractClass角色的模板方法中被调用。在示例程序中,由CharDisplay类和stringDisplay类扮演此角色。

如下图:

image-20200715173652647

4. 模式在Servlet中的应用

4.1 自己实现一下

浏览器向服务端发送一个请求,常用请求方式有两种,get请求和post请求,这两种请求方式会导致请求参数在请求协议包(Http包)中的位置是不一样的,那么请求协议包中不同的内容到达服务端之后会有不同的对象进行处理,如请求头的内容由tomcat负责,请求体中的内容由request负责,所以此时,开发人员在拿到service()方法后考虑到它可以接受所有请求方式,因此会针对不同的请求方式封装不同的请求方法。

建一个OneServlet 继承GenericServlet,实现service()方法,需要重写里面的doPost和doGet方法。

 public class OneServlet extends GenericServlet {
 
     @Override
     public void service(ServletRequest req, ServletResponse arg1) throws ServletException, IOException {
         
         
                 //1.从协议包【请求行】中来读取浏览器发送的请求方式
                 HttpServletRequest request = (HttpServletRequest)req;//一般来说由父类修饰的对象由子类来修饰对象,目的就是功能扩充
                 String method = request.getMethod();//POST GET
                 if("GET".equals(method)){
                     doGet(req, arg1);
                }else if("POST".equals(method)){
                     doPost(req, arg1);
                }    
    }
     //处理浏览器发送的post请求
     public void doPost(ServletRequest arg0, ServletResponse arg1){
           //这里面是doPost封装好的方法
         
           System.out.println("doPost is run....");
    }
     
     //处理浏览器发送的get请求
     public void doGet(ServletRequest arg0, ServletResponse arg1){
           //这里面是doPost封装好的方法
         
           System.out.println("doGet is run....");
    }
     
 }

现在开发人员面临的是,即需要做方法的实现,有需要考虑service()方法在何时调用。在实际开发过程中service()方法里面是一段重复性的代码,所有的servlet类实现中都需要写这么一段重复性的代码,这样重复的开发既增加工作量,又显得代码臃肿,降低了系统耦合度。模板方法设计模式就是来解决这个问题的。下面看一下怎么解决。

建立MyHttpServlet类(就是模板方法设计模式中的父类),继承GenericServlet类。

 public  class MyHttpServlet extends GenericServlet {
 
     @Override
     public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
         //控制子类中的doGet和doPost方法
         //1.从协议包【请求行】来读取浏览器发送的请求方式
         HttpServletRequest request = (HttpServletRequest)req;
         String method = request.getMethod();//POST GET
         if("GET".equals(method)){
             doGet(req, res);//this.doGet()
        }else if("POST".equals(method)){
             doPost(req, res);
        }
    }
     
     public  void doPost(ServletRequest arg0, ServletResponse arg1){
         
    }
     
     public void doGet(ServletRequest arg0, ServletResponse arg1){
         
    }
     
 }

建立TwoServlet类,此时此刻开发人员不用去考虑何时调用doGet方法。当调用TwoServlet类的时候,tomcat一定是调用它的service()方法。

/**
 * Servlet implementation class TwoServlet
 */
public class TwoServlet extends MyHttpServlet {
    
    //选择是接受doGet方法还是doPost方法
    
    @Override
    public void doGet(ServletRequest arg0, ServletResponse arg1) {
          System.out.println("ThreeServlet doGet is run...");
    }
    
}

测试代码localhost:8080/.../...

ThreeServlet doGet is run...

 

5、Tomcat源码之HttpServlet

HttpServlet也是继承了GenericServlet ,跟踪找到Service()方法,发现有两个service()方法。

    //这个方法是从它的父类GenericServlet继承过来的
    @Override
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
    {   
        HttpServletRequest  request;
        HttpServletResponse response;
        
        if (!(req instanceof HttpServletRequest &&
                res instanceof HttpServletResponse)) {
            throw new ServletException("non-HTTP request or response");
        }
        //分别对请求对象和响应对象做了类型强转。
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;

        service(request, response);//调用的是自己声明的service方法,重载。
    }
}

进入到自己声明的service()方法

  protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();//读取请求方式

        if (method.equals(METHOD_GET)) {//根据请求方式调用对应方法
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
            
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
            
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
            
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
            
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);
            
        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

发现service方法没有使用final,这是因为如果使用final修饰,就彻底断绝了我们下游开发人员的开发,这样是降低了系统的灵活度。

设计模式是问题解决思想(办法),没有固定的命令搭配 。

如果我们自己可以有这样一些解决办法,那就是好的设计模式。

公众号发哥讲

这是一个稍偏基础和偏技术的公众号,甚至其中包括一些可能阅读量很低的包含代码的技术文,不知道你是不是喜欢,期待你的关注。

代码分享

https://gitee.com/naimaohome

微信公众号 点击关于我,加入QQ群,即可获取到代码以及高级进阶视频和电子书!!

img

如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~

● 扫码关注我们

据说看到好文章不推荐的人,服务器容易宕机!

本文版权归 发哥讲博客园 共有,原创文章,未经允许不得转载,否则保留追究法律责任的权利。

posted @ 2020-07-21 09:59  发哥讲Java  阅读(168)  评论(0编辑  收藏  举报