模板方法模式定义了一个算法的步骤,并允许子类别为一个或多个步骤提供其实践方式。让子类别在不改变算法架构的情况下,重新定义算法中的某些步骤。
实现要素:
一、抽象基类
1.基本方法
如:
/**
* 基本方法 开幕式
*/
private void opening() {
System.out.println("运动会开幕式");
}
2.抽象方法(知道基本作用,不知道细节,需要延迟的子类中实现的)
如:
/**
* 抽象的基本方法,比赛
*/
protected abstract void game();
3、可选的钩子(通过子类复写控制模板方法中的其中一个步骤是否需要)
/**
* 询问用户是否需要加入演唱环节
* Hook,钩子函数,提供一个默认或空的实现
* 具体的子类可以自行决定是否挂钩以及如何挂钩
* @return
*/
protected boolean isNeedSing() {
return true;
}
4、Template方法(final)(不能被子类复写)
/**
* 开幕式的模板方法
* 封装了所有子类共同遵循的算法框架
*/
public final void prepareGamesTemplate(){
//开幕式
opening();
//歌曲演唱
if(isNeedSing()){
sing();
}
//比赛
game();
//闭幕式
closing();
}
二、具子类
1、实现基类中的抽象方法
public class FootballGames extends Games {
@Override
protected void game() {
System.out.println("进行足球比赛");
}
}
2、覆盖钩子方法(根据需求选择是否加入)
@Override
protected boolean isNeedSing() {
return false;
}
要素总结:准备一个抽象类,将部分逻辑以具体的方法的形式实现,然后声明一些抽象方法交由子类实现剩余逻辑,用钩子方法给予子类更大的灵活性。最后将方法汇总构成一个不可改变的模板方法。
适应场景
1、算法或操作遵循相似的逻辑
2、重构时(把相同的代码抽取到父类中)
3、重要、复杂的算法,核心算法设计为模板算法
优点
1、封装性好
2、复用性好
3、屏蔽细节
4、便于维护
缺点
java语言是单继承语言,对已有继承的类重构时十分困难。
模板方法模式和策略模式的区别
在同一需求下两种模式都存在可以使用的情况。
模板方法的意图:定义一个算法流程,将一些特定步骤的具体实现、延迟到子类。使得可以在不改变算法流程的情况下,通过不同的子类、来实现“定制”流程中的特定的步骤。
策略模式的意图:使不同的算法可以被相互替换,而不影响客户端的使用。
在意图上看,模板方法更加强调:
1)定义一条线(算法流程),线上的多个点是可以变化的(具体实现在子类中完成),线上的多个点一定是会被执行的,并且一定是按照特定流程被执行的。
2)算法流程只有唯一的入口,对于点的访问是受限的【通常用受保护的虚函数来定义可变点】。
策略模式更注重于: 一个“策略”是一个整体的(完整的)算法,算法是可以被整体替换的。而模板方法只能被替换其中的特定点,算法流程是固定不可变的。
个人认为策略模式中“多用组合,少用继承”思想,好像彰显其相对优越性。
最后附上完整代码:
抽象基类
public abstract class Games {
/**
* 开幕式的模板方法
* 封装了所有子类共同遵循的算法框架
*/
public final void prepareGamesTemplate(){
//开幕式
opening();
//歌曲演唱
if(isNeedSing()){
sing();
}
//比赛
game();
//闭幕式
closing();
}
/**
* 询问用户是否需要加入演唱环节
* Hook,钩子函数,提供一个默认或空的实现
* 具体的子类可以自行决定是否挂钩以及如何挂钩
* @return
*/
protected boolean isNeedSing() {
return true;
}
/**
* 基本方法 闭幕式
*/
private void closing() {
System.out.println("运动会闭幕式");
}
/**
* 抽象的基本方法,比赛
*/
protected abstract void game();
/**
* 抽象的基本方法,歌唱表演
*/
protected abstract void sing();
/**
* 基本方法 开幕式
*/
private void opening() {
System.out.println("运动会开幕式");
}
}
子类(其中一个复写了钩子算法)
public class FootballGames extends Games {
@Override
protected void game() {
System.out.println("进行足球比赛");
}
@Override
protected void sing() {
System.out.println("演唱“足球世界杯”歌曲");
}
}
public class BasketballGames extends Games {
@Override
protected void game() {
System.out.println("进行篮球比赛");
}
@Override
protected void sing() {
System.out.println("演唱歌曲");
}
@Override
protected boolean isNeedSing() {
return false;
}
}
测试类
public class Client {
public static void main(String[] args) {
System.out.println("早上运动会开始");
Games g1=new BasketballGames();
g1.prepareGamesTemplate();
System.out.println("运动会结束");
System.out.println("______________________________________________");
System.out.println("早上运动会开始");
Games g2=new FootballGames();
g2.prepareGamesTemplate();
System.out.println("运动会结束");
}
}