实用的设计模式–模板方法模式

>>2020,微服务装逼指南

模板方法模式是我工作中用到最多的模式,这个模式的类图比较简单,而且思路也比较简单,只要有重复的工作,加以抽象,都可以使用模板方法。

模板方法的前提

道理很好讲,但是很多人依旧会问,为什么他的编码环境却没有这样的使用情况。

设计模式是基于面向对象的套路,所以脱离不了抽象这个概念,很多情况之所以没办法去模式化主要是因为无法抽象。

我们做一件事情,都是分步骤的,例如去煮咖啡,先准备咖啡,然后水加热,放咖啡豆,结束后装入容器。针对这件事情,并不能有什么想法,抽象的基础是找共同点,起码是两个事物之间的影响的结果,如果还有一件事情是煮茶叶,那么过程就是准备茶叶,然后水加热,放茶叶,结束后装入容器。那么明显可以抽象煮东西的这个过程。准备材料,水加热,放材料,结束后装入容器。但是另外一件事情是砍柴,那么这样貌似就不好找细节上的共同点了。很多情况下,没有模式主要是场景的问题,编码就写一个场景,共同点不好找,纯粹靠想象去抽象,可能模式是用上了,但是没有普及性。效果基本和没有模式差不多。

类图

实用的设计模式--模板方法模式

模板方法模式的主要的思想就是定制一个流程,每个实现类都去遵循这个流程。这个流程是需要通过具体场景去抽象的。

常见案例

最开始学习web编程的时候都会接触HttpServlet,写自己的servlet的时候,要继承这个类,去重写doget和dopost。这里用的就是模板方法模式。具体的流程就在service方法中。

  protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
  
{
    String method = req.getMethod();
    //get
    if (method.equals("GET"))
    {
      long lastModified = getLastModified(req);
      if (lastModified == -1L)
      {
        doGet(req, resp);
      }
      else
      {
        long ifModifiedSince;
        try
        {
          ifModifiedSince = req.getDateHeader("If-Modified-Since");
        }
        catch (IllegalArgumentException iae)
        {
          long ifModifiedSince;
          ifModifiedSince = -1L;
        }
        if (ifModifiedSince < lastModified / 1000L * 1000L)
        {
          maybeSetLastModified(resp, lastModified);
          doGet(req, resp);
        }
        else
        {
          resp.setStatus(304);
        }
      }
    }
    //head
    else if (method.equals("HEAD"))
    {
      long lastModified = getLastModified(req);
      maybeSetLastModified(resp, lastModified);
      doHead(req, resp);
    }
    //post
    else if (method.equals("POST"))
    {
      doPost(req, resp);
    }
    //put
    else if (method.equals("PUT"))
    {
      doPut(req, resp);
    }
    //delete
    else if (method.equals("DELETE"))
    {
      doDelete(req, resp);
    }
    //options
    else if (method.equals("OPTIONS"))
    {
      doOptions(req, resp);
    }
    //trace
    else if (method.equals("TRACE"))
    {
      doTrace(req, resp);
    }
    else
    {
      String errMsg = lStrings.getString("http.method_not_implemented");
      Object[] errArgs = new Object[1];
      errArgs[0] = method;
      errMsg = MessageFormat.format(errMsg, errArgs);

      resp.sendError(501, errMsg);
    }
  }

在service中对http支持的7中方式都做了处理,剩下的就是每个servlet根据自己的情况覆写doGet,doHead,doPost,doPut,doDelete,doOptions,doTrace。具体的业务也写在具体的方法里,具体是调用的是哪种请求方法由service方法中确认。

思想类似的产物

java的jdbc的操作都是获取连接,创建statment或者preparestatement,然后执行sql,然后关闭resultset(如果有的话),关闭statement,然后关闭连接。具体和业务相关的其实就是sql,剩下的都是流程化的过程。spring jdbc template就是模板化了这些过程,不同的是,他使用了组合的方式去调用而不是继承,严格意义上不是标准的模板方法,只能说是思想类似。

实战场景

工作中遇到一个场景就是用模板方法来做一个try catch的异常模板。

场景

最开始写的代码出现一个问题,就是有异常没有处理的话,逻辑都被中断了。因此需要加入异常处理。要这样改的地方还有很多。

代码模板

public class Template {

    public static void main(String[] args) {

        new ExceptionTemplate(){
            @Override
            protected void toDo() {
                //流程
                System.out.println("over");
            }
        }.doWork();

    }

}
//模板类
abstract class ExceptionTemplate{
    public final void doWork(){
         try{
             toDo();
         }catch(Exception e){
             //异常处理方式
         }

    }
    protected  void toDo(){}
}

好处

每个地方的改动都是有限的,都是new一个匿名内部类,调用一下方法,原来的写的代码都移动到抽象类方法中,提交的代码的变动相对比较少,而且不容易出错,逻辑都在模板里,只要想好一个就可以了。


原文始发于微信公众号(一次编译多处运行):实用的设计模式--模板方法模式