模板方法模式
先来举个例子,两个人下中国象棋,要怎么实现?这简单啊,创建一个“棋手”的类,实例化两个对象,放一个循环里,每次都调用它们“走棋”的方法就行了。
写段伪代码:
class Player{
public Position getPosition(){
//返回要走棋的位置
}
public void put(Position p){
//在一个位置上放棋子
}
public boolean isWin(){
//判断是否胜利
}
public boolean play(){
Position p = getPosition();
put(p);
return isWin();
}
}
public class Client{
public static void main(String[] args){
Player p1 = new Player();
Player p2 = new Player();
boolean win = false;
while(true){
win = p1.play();
if(win)
break;
win = p2.play();
if(win)
break;
}
}
}
额,需求变了,不玩中国象棋了,整围棋。怎么办?在创建一个基本一样的类?不需要!可以看到,所有的棋类游戏都可以用上面的“通式(play()方法)”来解决。所以,设计如下的解决方法:
注意观察上面的场景类的main方法,只调用了play这一个方法,所以对于其他方法,可以不对外开放,有保证可以过载(Override),所以使用protected修饰。play方法被final修饰,为什么?因为不论是象棋还是围棋,处理的流程不变,也就是说,算法的框架没发生变化,再直白点,就是play方法在两种情况都一样,不一样的就是其他的方法。
进入主题
模板方法模式,什么是“模板”?就是“死”的,也就是“死方法”,也就是说,执行特定步骤的方法,就比如上面的play方法。
模板方法模式的定义
模板方法模式,定义一个操作中的算法框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可冲定义该算法的某些特定步骤。
注意抽象类AbstractClass中的方法,有若干没有实现的抽象方法(定义中说的“特定步骤”),有一个不允许过载(Override)的“死方法”(模板方法,定义中说的算法框架)。子类只需要实现那些“特定步骤”即可
使用“钩子(Hook)函数”扩展模板方法
引入这个扩展的目的,子类间的模板行为“不相同”(注意这里的不相同是打引号的,因为模板方法是一样的,虽然表现不同,但是行为本质上是相同的),可以使用钩子函数限制特定步骤的执行,假设有下面的抽象模板类
class AbstractTamplate{
protected void method1();
protected void method2();
protected void method3();
public final void templateMethod(){
method1();
method2();
method3();
}
}
假设现在有两个子类的行为不同,第一个子类只要执行method1和method3,而第二个子类只要执行method2和method3
class AbstractTamplate{
protected void method1();
protected void method2();
protected void method3();
protected boolean hook1();
protected boolean hook3();
public final void templateMethod(){
if(hook1())
method1();
if(hook2())
method2();
method3();
}
}
//类A不执行method2,可以让hook2()返回false
class A extends AbstractTemplate{
//其他的实现就不写了
...
boolean hook1(){
return true;
}
boolean hook2(){
return false;
}
...
}
//类B不执行method1,可以让hook1()返回false
class B extends AbstractTemplate{
//其他的实现就不写了
...
boolean hook1(){
return false;
}
boolean hook2(){
return true;
}
...
}
优缺点以及应用场景
优点
- 封装不变部分,扩展可变部分
- 提取公共部分代码,易于维护
- 行为由父类控制,子类实现。也可以说成是父类调用子类方法,符合开闭原则
缺点
设计抽象,难理解,可读性不高
适用场景
- 多个子类有公共的方法,并且逻辑基本相同
- 重要、复杂的算法,可以把核心算法设计为模板方法,特定步骤交给各个子类实现。
- 代码重构,把相同的代码抽取到父类中,然后通过钩子函数约束其行为
- 框架的设计,举个都熟悉的例子,Servlet中的Service方法,就是一个“特定步骤”,使用Servlet就是要继承它,然后实现Service方法,因为它的父类要调用这个方法。
总结
模板方法,最主要的就是将行为延迟到子类。