设计模式之模板方法模式

定义

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
例如我们去银行办理业务,要经过取号、排队、办理具体业务等过程,取号、排队对于每个客户都是一样的,可以在父类中实现,
办理具体业务可能每个人都不同,可能是取款,存款,转账,这个操作可以延迟到子类中实现。

结构

  • AbstractClass,抽象类,用来定义算法骨架
  • ConcreteClass,具体实现类,用来实现算法骨架中的某些步骤,完成与特定子类相关的功能。

简单实现

抽象类

public abstract class AbstractClass {

  public void operator() {
    beforeOperator();
    System.out.println("operator");
    afterOperator();
  }

  public abstract void beforeOperator();

  public abstract void afterOperator();
}

具体实现类

public class ConcreteClass extends AbstractClass {

  @Override
  public void beforeOperator() {
    System.out.println("ConcreteClass beforeOperator");
  }

  @Override
  public void afterOperator() {
    System.out.println("ConcreteClass afterOperator");
  }
}

模板方法模式在JDK和Servlet中的实现

JDK中实现

JDK中的AbstractList,AbstractMap

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
  //添加集合
  public boolean addAll(Collection<? extends E> c) {
        boolean modified = false;
        for (E e : c)
            if (add(e))
                modified = true;
        return modified;
    }
    //添加单个元素
    public boolean add(E e) {
        throw new UnsupportedOperationException();
    }

}

AbstractList中有很多模板方法,这里我们就以addAll()方法为例,子类只需要实现add()方法就可以了。

Servlet中的实现

Servlet中的HttpServlet

public abstract class HttpServlet extends GenericServlet{
  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);
        }
    }

  protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException{
    }

   protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException{
    }

HttpServlet的service方法定义整个算法骨架,根据请求方法来做相应的处理,比如请求方法为GET,就调用doGet方法来处理,子类只需要重写这些方法就可以了。

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class FirstServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    resp.getWriter().println("hello");
  }
}

总结

优点

  1. 将公共的功能提取到了父类中,实现了代码的复用。
  2. 将不变的部分封装到父类中,可变部分由子类继承实现,便于子类继续扩展。

缺点

  1. 算法骨架不容易升级,可能会影响所有相关的子类。

本质

模板方法模式的本质是固定算法骨架。通过固定算法骨架来约束子类的行为,并在特定的扩展点来让子类进行功能扩展,
从而让程序既有很好的复用性,又有较好的扩展性。

使用场景

  1. 各个子类中有公共行为,为了避免代码重复,应该提取到父类实现。
  2. 需要固定算法骨架,并要子类来实现一些可变的部分。

参考

大战设计模式【11】—— 模板方法模式
设计模式的征途—17.模板方法(Template Method)模式
设计模式(十四)——模板模式(SpringIOC源码分析)
设计模式——模板方法模式
模板方法模式(模板方法设计模式)详解
研磨设计模式-书籍

posted @ 2021-08-29 11:58  strongmore  阅读(56)  评论(0编辑  收藏  举报