1 概述
1.1定义
定义一个操作中的算法的框架,而将一些步骤延伸到子类中。使得子类可以不改变算法结构即可重新定义该算法的某些特定步骤。
类图如下:

模板抽象类中定义基本操作,由子类实现,然后在模板方法中被调用,实现对基本方法的调度,完成固定的逻辑。一般基本方法都由protected修饰,尽量不扩大父类中的访问权限。模板方法不可修改,尽量使用final修饰。完整模板方法模式还应该加入一个钩子函数,可以由子类决定部分执行结果。
1.2 优缺点
优点:封装不变部分,拓展可变部分;提取公共代码方便维护;行为由父类控制,子类实现。
缺点:子类执行结果影响了父类执行结果,也就是子类对父类产生了影响,在复杂的项目中,增加代码阅读的难度。
1.3 使用场景
多个子类有共有的方法,并且实现逻辑基本相同时。
重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能由子类实现。
重构时,把相同代码抽取到父类中,然后通过钩子函数约束其行为。
2 代码示例
/**
 * @author chiangtaol
 * @date 2020-11-21
 * @describe 对战游戏抽象类
 */
public abstract class AbstractPlayGame {
    /**
     * 匹配玩家
     */
    protected abstract void matchPlayers();
    /**
     * 选择英雄
     */
    protected abstract void chooseHeroes();
    /**
     * 选择皮肤
     */
    protected abstract void chooseSkin();
    /**
     * 开始对战
     */
    protected abstract void begin();
    /**
     * 结束对局
     */
    protected abstract void end();
    /**
     * 是否有钱
     *
     * @return /
     */
    protected boolean isRich(){
        return false;
    }
    final public void play() {
        this.matchPlayers();
        this.chooseHeroes();
        // 有钱可以选皮肤,没钱老老实实用默认皮肤
        if (this.isRich()) {
            this.chooseSkin();
        }
        this.begin();
        this.end();
    }
}/**
 * @author chiangtaol
 * @date 2020-11-21
 * @describe 英雄联盟游戏
 */
public class LeagueOfLegends extends AbstractPlayGame {
    protected boolean rich = false;
    @Override
    public boolean isRich() {
        return this.rich;
    }
    public void setRich(boolean rich) {
        this.rich = rich;
    }
    /**
     * 匹配玩家
     */
    @Override
    protected void matchPlayers() {
        System.out.println("匹配到10个人...");
    }
    /**
     * 选择英雄
     */
    @Override
    protected void chooseHeroes() {
        System.out.println("选择操作英雄");
    }
    /**
     * 选择皮肤
     */
    @Override
    protected void chooseSkin() {
        System.out.println("选择终极皮肤");
    }
    /**
     * 开始对战
     */
    @Override
    protected void begin() {
        System.out.println("全军出击...");
    }
    /**
     * 结束对局
     */
    @Override
    protected void end() {
        System.out.println("游戏结束");
    }
}下面测试一下,根据钩子方法去改变执行结果
/**
 * @author chiangtaol
 * @date 2020-11-24
 * @describe
 */
class AbstractPlayGameTest {
    @Test
    void play() throws Exception{
        LeagueOfLegends playGame = new LeagueOfLegends();
        //playGame.setRich(true);
        playGame.play();
    }
}
//匹配到10个人...
//选择操作英雄
//全军出击...
//游戏结束
//匹配到10个人...
//选择操作英雄
//选择终极皮肤
//全军出击...
//游戏结束