0
点赞
收藏
分享

微信扫一扫

设计模式之禅(四):模版方法模式,介绍+demo+HttpServlet中的模版方法运用

小黑Neo 2022-01-16 阅读 23

𝑰’𝒎 𝒉𝒉𝒈, 𝑰 𝒂𝒎 𝒂 𝒈𝒓𝒂𝒅𝒖𝒂𝒕𝒆 𝒔𝒕𝒖𝒅𝒆𝒏𝒕 𝒇𝒓𝒐𝒎 𝑵𝒂𝒏𝒋𝒊𝒏𝒈, 𝑪𝒉𝒊𝒏𝒂.

  • 🏫 𝑺𝒉𝒄𝒐𝒐𝒍: 𝑯𝒐𝒉𝒂𝒊 𝑼𝒏𝒊𝒗𝒆𝒓𝒔𝒊𝒕𝒚
  • 🌱 𝑳𝒆𝒂𝒓𝒏𝒊𝒏𝒈: 𝑰’𝒎 𝒄𝒖𝒓𝒓𝒆𝒏𝒕𝒍𝒚 𝒍𝒆𝒂𝒓𝒏𝒊𝒏𝒈 𝒅𝒆𝒔𝒊𝒈𝒏 𝒑𝒂𝒕𝒕𝒆𝒓𝒏, 𝑳𝒆𝒆𝒕𝒄𝒐𝒅𝒆, 𝒅𝒊𝒔𝒕𝒓𝒊𝒃𝒖𝒕𝒆𝒅 𝒔𝒚𝒔𝒕𝒆𝒎, 𝒎𝒊𝒅𝒅𝒍𝒆𝒘𝒂𝒓𝒆 𝒂𝒏𝒅 𝒔𝒐 𝒐𝒏.
  • 💓 𝑯𝒐𝒘 𝒕𝒐 𝒓𝒆𝒂𝒄𝒉 𝒎𝒆:𝑽𝑿
  • 📚 𝑴𝒚 𝒃𝒍𝒐𝒈: 𝒉𝒕𝒕𝒑𝒔://𝒉𝒉𝒈𝒚𝒚𝒅𝒔.𝒃𝒍𝒐𝒈.𝒄𝒔𝒅𝒏.𝒏𝒆𝒕/
  • 💼 𝑷𝒓𝒐𝒇𝒆𝒔𝒔𝒊𝒐𝒏𝒂𝒍 𝒔𝒌𝒊𝒍𝒍𝒔:𝒎𝒚 𝒅𝒓𝒆𝒂𝒎

1:模版方法

1-1:定义

☘️大白话说,就是父类中有一个方法调用抽象方法step1,抽象方法step2… ,来完成某一个逻辑。这种方法就是模版方法,step1,step2是抽象方法。也就是说,模版方法定义这些抽象方法执行顺序,把你安排起来,并且子类别不允许覆写模板方法。具体方法怎么实现是子类自己实现的。形象的来说,我定义了一共99八十一难,每一难怎么过,你自己看着办。

1-2:适用场景

有一个设计模式,我们就应该想一下为什么要有模版方法模式?

  • 把子类公共部分的代码抽取出来。
  • 定规矩,流程怎么走,我说了算,你不能改,都得按照我这个流程走。

1-3:简单demo

在这里插入图片描述

  • GoOnlineHelper
    package com.company.design.template;
    
    public abstract class GoOnlineHelper {
        abstract void login();
    
        abstract void scan();
    
        abstract void logout();
    
        protected final void surfTheInternet() {
            this.login();
            this.scan();
            this.logout();
        }
    }
    
  • chromeHelper
    package com.company.design.template;
    
    public class ChromeHelper extends GoOnlineHelper {
        @Override
        void login() {
            System.out.println("chrome help you login");
        }
    
        @Override
        void scan() {
            System.out.println("chrome help you scan");
        }
    
        @Override
        void logout() {
            System.out.println("chrome help you logout");
        }
    }
    
  • firefoxHelper
    package com.company.design.template;
    
    public class FireFoxHelper extends GoOnlineHelper {
        @Override
        void login() {
            System.out.println("firefox help you login");
        }
    
        @Override
        void scan() {
            System.out.println("firefox help you scan");
        }
    
        @Override
        void logout() {
            System.out.println("firefox help you logout");
        }
    }
    

☘️GoOnlineHelper 抽象类定义了3个动作以及模版surfTheInternet规定了3者的顺序,从而实现类只需要去实现流程中的细节部分,流程已经被父抽象类定义好了。那么,怎么去理解子类可以重新定义某些特定的步骤呢?比如chrome不想logout。可以加一个标识,来标识是否想要logout。于是乎,有了下面的代码:

  • GoOnlineHelper
    package com.company.design.template;
    
    public abstract class GoOnlineHelper {
    
        boolean logoutFlag;
    
        GoOnlineHelper(boolean logoutFlag) {
            this.logoutFlag = logoutFlag;
        }
    
        boolean logoutOrNot() {
            return this.logoutFlag;
        }
    
        abstract void login();
    
        abstract void scan();
    
        abstract void logout();
    
        protected final void surfTheInternet() {
            this.login();
            this.scan();
            if (logoutOrNot()) {
                this.logout();
            }
        }
    
        public boolean isLogoutFlag() {
            return logoutFlag;
        }
    
        public void setLogoutFlag(boolean logoutFlag) {
            this.logoutFlag = logoutFlag;
        }
    }
    
  • chromeHelper
    package com.company.design.template;
    
    public class ChromeHelper extends GoOnlineHelper {
    
        public ChromeHelper(boolean logoutFlag) {
            super(logoutFlag);
        }
    
        @Override
        void login() {
            System.out.println("chrome help you login");
        }
    
        @Override
        void scan() {
            System.out.println("chrome help you scan");
        }
    
        @Override
        void logout() {
            System.out.println("chrome help you logout");
        }
    }
    
  • firefoxHelper
    package com.company.design.template;
    
    public class FireFoxHelper extends GoOnlineHelper {
        public FireFoxHelper(boolean logoutFlag) {
            super(logoutFlag);
        }
    
        @Override
        void login() {
            System.out.println("firefox help you login");
        }
    
        @Override
        void scan() {
            System.out.println("firefox help you scan");
        }
    
        @Override
        void logout() {
            System.out.println("firefox help you logout");
        }
    }
    
  • client
    package com.company.design.template;
    
    public class Client {
        public static void main(String[] args) {
            GoOnlineHelper chromeHelper = new ChromeHelper(true);
            GoOnlineHelper firefoxHelper = new FireFoxHelper(false);
            chromeHelper.surfTheInternet();
            firefoxHelper.surfTheInternet();
        }
    }	
    ----output----
    chrome help you login
    chrome help you scan
    chrome help you logout
    firefox help you login
    firefox help you scan
    

☘️可以发现,通过子类传递的标识,就可以修改部分的流程逻辑,这里Firefox 就少了一回登录,跳过了一次流程。

1-4:模版方法的缺陷

  • 模版方法模式的调用顺序和流程都是被模版方法限制的,这就很好的体现了“模版”二字,你只能按照这个流程走。
  • 违背了里式替换原则,因为父类中帮助了子类做了一个default实现。
  • 因为算法流程都在模版方法里面,如果这个算法的流程越来越长,那么维护就会越来越难了。

1-5:源码中模版方法-HttpServlet

  • HttpServlet

    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);
          }
      }
    

    它这个就是一个模版方法,我们看一眼doGet和doPost

    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
          throws ServletException, IOException
      {
          String protocol = req.getProtocol();
          String msg = lStrings.getString("http.method_get_not_supported");
          if (protocol.endsWith("1.1")) {
              resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
          } else {
              resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
          }
      }
    
      protected void doPost(HttpServletRequest req, HttpServletResponse resp)
          throws ServletException, IOException
      {
          String protocol = req.getProtocol();
          String msg = lStrings.getString("http.method_post_not_supported");
          if (protocol.endsWith("1.1")) {
              resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
          } else {
              resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
          }
      }
    

☘️会发现虽然这里有默认实现,但是都是一样的,也就是说这里是等着你去覆盖,这就是为啥我们之前写servlet的时候,extend HttpServlet之后,要覆盖doGet和doPost方法的原因了,这里是一个模版方法模式,我们覆盖具体的方法,执行流程在HttpServlet中已经帮我们定好了。
在这里插入图片描述

举报

相关推荐

0 条评论