概述:定义一个操作中的算法骨架,将步骤延迟到子类中去。模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤。
角色:
- 抽象类:实现模板方法,定义算法的骨架
- 具体类:实现抽象类中的方法,完善具体算法
- 钩子方法:钩子方法由一个抽象类或具体类声明并实现,而其子类可能会加以扩展。通常在父类中给出的实现是一个空实现(可使用virtual关键字将其定义为虚函数),并以该空实现作为方法的默认实现,当然钩子方法也可以提供一个非空的默认实现。
类图:
现在设计一个需求:老板和职员都去老板开的餐厅吃饭,老板要点单吃饭但是不要给钱。职员要给钱。
抽象方法:
public abstract class EatOutside {
public void templateMethod(){
order();
eat();
if(this.isFree()){
pay();
}
}
private void order(){
System.out.println("先点单");
}
protected abstract void eat();
private void pay(){
System.out.println("最后买单");
}
/**
* 钩子方法。默认要买单
* @return
*/
protected boolean isFree(){
return true;
}
}
然后老板吃具体实现:
public class BossEat extends EatOutside {
//老板逼格高咯
public void eat() {
System.out.println("我是老板,我躺着吃");
}
/**
* 来办来自己餐厅吃饭,要钱?肯定不要啊
* @return
*/
public boolean isFree(){
return false;
}
}
然后员工吃具体实现:
public class StaffEat extends EatOutside {
public void eat() {
System.out.println("我是普通职员,我只能坐着吃");
}
/**
* 然后就不要重载钩子方法啦,因为 你肯定要给钱的嘛,默认是需要买单的
*/
}
最后测试:
public static void main(String[] args) {
EatOutside be = new BossEat();
be.templateMethod();
System.out.println("咯咯咯咯咯");
EatOutside se = new StaffEat();
se.templateMethod();
}
打印输出:
先点单
我是老板,我躺着吃
咯咯咯咯咯
先点单
我是普通职员,我只能坐着吃
最后买单
优点:
- 将公共方法搬到抽象类,减少子类代码重复性
- 子类实现某些方法的细节,有利于扩展
- 通过父类调用子类实现操作,通过子类扩展新行为,符合“ 开放封闭原则”
缺点:
- 每个不同的类都要实现一个子类,导致子类增加,设计更加抽象
场景:
- 在某些类的算法中,用了相同的方法,造成代码的重复。
- 控制子类扩展,子类必须遵守算法规则