模板方法模式
定义:定义一个算法的骨架,并允许子类为其中的一个或多个步骤提供实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤
适用场景
- 一次性实现一个算法不变的部分,并将可变的行为留给子类来实现
- 各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复
优点
- 利用模板方法将相同处理逻辑的代码放到抽象父类中,可以提高代码的复用性
- 将不同的代码放在不同的子类中,通过对子类的扩展来增加新的行为,提高代码的扩展性
- 把不变的行为写在父类,能有效去除子类的重复代码,提高代码复用性,符合开闭原则
缺点
- 每一个抽象类都需要一个子类来实现,导致类数目增加,间接增加了系统的复杂度
- 继承关系本身的缺点,如果父类增加新的抽象方法,所有子类都需要修改一遍
钩子方法
设计钩子方法的主要目的是用来干预执行流程,使得我们控制行为流程更加灵活,更加符合实际的业务需求。此外,钩子方法的返回值一般为适合条件分支语句的 int 、boolean 等
比如:上课->布置作业->检查是否需要布置作业->是,则布置作业;否,则结束流程。这其中的 检查是否需要布置作业 就是系统中的一个钩子方法,可以用于干预执行流程
示例
以课程为例
课程的抽象基类,定义了一个上课的完整流程,提供了钩子方法给子类干预流程,子类也可以覆盖其中某一个步骤的方法
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的执行流程
