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个人...
//选择操作英雄
//选择终极皮肤
//全军出击...
//游戏结束