Skip to content

模板方法模式

定义:定义一个算法的骨架,并允许子类为其中的一个或多个步骤提供实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤

适用场景

  1. 一次性实现一个算法不变的部分,并将可变的行为留给子类来实现
  2. 各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复

优点

  1. 利用模板方法将相同处理逻辑的代码放到抽象父类中,可以提高代码的复用性
  2. 将不同的代码放在不同的子类中,通过对子类的扩展来增加新的行为,提高代码的扩展性
  3. 把不变的行为写在父类,能有效去除子类的重复代码,提高代码复用性,符合开闭原则

缺点

  1. 每一个抽象类都需要一个子类来实现,导致类数目增加,间接增加了系统的复杂度
  2. 继承关系本身的缺点,如果父类增加新的抽象方法,所有子类都需要修改一遍

钩子方法

设计钩子方法的主要目的是用来干预执行流程,使得我们控制行为流程更加灵活,更加符合实际的业务需求。此外,钩子方法的返回值一般为适合条件分支语句的 intboolean

比如:上课->布置作业->检查是否需要布置作业->是,则布置作业;否,则结束流程。这其中的 检查是否需要布置作业 就是系统中的一个钩子方法,可以用于干预执行流程

示例

以课程为例

课程的抽象基类,定义了一个上课的完整流程,提供了钩子方法给子类干预流程,子类也可以覆盖其中某一个步骤的方法

java
public abstract class AbastractCourse {
    
    public final void createCourse(){
        //1、发布预习资料
        postPreResoucse();
        
        //2、制作课件
        createPPT();
        
        //3、直播授课
        liveVideo();
        
        //4、上传课后资料
        postResource();
        
        //5、布置作业
        postHomework();
        
        if(needCheckHomework()){
            checkHomework();
        }
    }

    protected abstract void checkHomework();

    //钩子方法
    protected boolean needCheckHomework(){return  false;}

    protected void postHomework(){
        System.out.println("布置作业");
    }

    protected void postResource(){
        System.out.println("上传课后资料");
    }

    protected void liveVideo(){
        System.out.println("直播授课");
    }

    protected void createPPT(){
        System.out.println("制作课件");
    }

    protected void postPreResoucse(){
        System.out.println("发布预习资料");
    }

}

具体的Java课程,设置 needCheckHomework 属性来使用钩子方法

java
public class JavaCourse extends AbastractCourse {
    private boolean needCheckHomework = false;

    public void setNeedCheckHomework(boolean needCheckHomework) {
        this.needCheckHomework = needCheckHomework;
    }

    @Override
    protected boolean needCheckHomework() {
        return this.needCheckHomework;
    }

    protected void checkHomework() {
        System.out.println("检查Java作业");
    }
}

具体的Python课程

java
public class PythonCourse extends AbastractCourse {
    protected void checkHomework() {
        System.out.println("检查Python作业");
    }
}

模板方法模式在源码中的体现

AbstractList

其中 get() 方法是一个抽象方法,在 AbstractList 中会有使用到 get() 方法,但它的逻辑交由子类来实现,调用时不需要关心具体实现逻辑

java
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
	abstract public E get(int index);  
}

HttpServlet

回想以前刚开始学习Web开发的时候,都是继承 HttpServlet 然后重写其中的 doGet()doPost() 方法,而在父类 HttpServlet 中这两个方法是没有具体有意义的实现,需要子类来自行实现,这也体现了模板方法模式

java
public abstract class HttpServlet extends GenericServlet {
	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(405, msg);
    } else {
      resp.sendError(400, 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(405, msg);
    } else {
      resp.sendError(400, msg);
    }

  }
}

MyBatis

在MyBatis框架中也有体现,在 BaseExecutor 类中,它是一个基础的SQL执行类,实现了大部分执行SQL的逻辑,然后有几个方法留给子类定制化实现

java
public abstract class BaseExecutor implements Executor {
  protected abstract int doUpdate(MappedStatement var1, Object var2) throws SQLException;

  protected abstract List<BatchResult> doFlushStatements(boolean var1) throws SQLException;

  protected abstract <E> List<E> doQuery(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, BoundSql var5) throws SQLException;

  protected abstract <E> Cursor<E> doQueryCursor(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4) throws SQLException;
}

那这些 doUpdate()doQuery() 等方法就是由它的子类来实现的,不同子类有不同的实现方法,但整体上来说,不影响整个SQL的执行流程