我们先来谈谈 "模板" 的概念,什么是模板? 很简单嘛,直接上图!
这就是一个模板!它定义了基本的样式,整个文档结构,内容大纲,而你要做的,就是再某些地方写上自己的内容就可以了! 让我们用代码来举一个模板方法的例子吧!
我们拟定一个炒米粉的过程(因为我今天吃的是米粉),首先,要刷锅,然后放油,放入粉条,翻炒, 放盐,接下来可以选择放醋,放辣椒,放葱, 然后起锅放入碗中, 自此整个流程完毕!
我们来分析一下这个过程:
通过画图,我们发现,其中某些步骤是完全固定的,那我们就可以思考,是不是可以有一个类,然后定义一个总的方法,这个方法就像是一个大的算法(把炒米粉看成是一个算法吧)骨架,或者说它就是一个模板,在这个方法中,它顺序调用不同的方法(步骤) , 先调用刷锅(),再放油()...接下来要解决放粉条的问题,怎么办,它自己不知道要放什么粉条,OK,抽象嘛,让子类来决定要放什么粉条咯! 再接下来要放醋...到底放不放呢? 放醋的过程是固定的,我们要复用这段代码,那怎么办呢? 那具体放醋过程还是我们父类自己觉得,而到底要不要放我们则可以问一下子类嘛!当它回答说不放,那就不放!
Ok,思路已经很清晰了!我们来让代码说话吧!
先来几个面和粉的对象...
/**所有粉条父类*/
public abstract class Flour {
protected String name ;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
/**挂面*/
class NoodlesFlour extends Flour{
public NoodlesFlour() {
this.name = "挂面";
}
}
/**米粉*/
class RiceFlour extends Flour{
public RiceFlour() {
this.name = "米粉";
}
}
接下来定义我们的模板类!
/**定义一个炒粉抽象模板方法类*/
abstract class AbstractFryFlourTemplate{
//定义为final,因为算法过程(炒粉过程)是固定的!我希望算法过程不被子类覆盖!
public final void startFry(){
rinse();
addOil();
addFlour();
roll();
addSalt();
//仔细看看,这部分像不像是一个钩子?
if( isAddVinegar() ){
addVinegar();
}
if( isAddPepper() ){
addPepper();
}
addToBowl();
}
private final void rinse(){
System.out.println("刷锅...");
}
private final void addOil(){
System.out.println("放点油...");
}
//抽象方法,放入米粉
protected abstract void addFlour();
//翻炒方法,子类可以根据炒法不同而进行覆盖
protected void roll(){
System.out.println("默认慢慢的炒...");
}
private final void addSalt(){
System.out.println("放点盐...");
}
//放醋方法
private final void addVinegar(){
System.out.println("打开醋瓶");
System.out.println("放点醋...");
System.out.println("关好醋瓶");
}
//放辣椒方法
private final void addPepper(){
System.out.println("放点辣椒!");
}
private final void addToBowl(){
System.out.println("放到碗里去...");
}
//到底放不放醋,子类可以来做决定,但我们默认放!
protected boolean isAddVinegar(){
return true;
}
//到底放不放辣椒,必须子类做出决定,也就是必须问下客户,您要辣椒不?
protected abstract boolean isAddPepper();
}
再定义两个不同的算法类...
/**炒面的类*/
class FlyNoodlesFlour extends AbstractFryFlourTemplate{
@Override
protected void addFlour() {
Flour flour = new NoodlesFlour();
System.out.println("放入:"+flour.getName());
}
//客人说要放辣椒
@Override
protected boolean isAddPepper() {
return true;
}
//覆盖翻炒方法,我是高级厨师...
@Override
protected void roll() {
System.out.println("各种花样百出的炒...");
}
}
/**炒粉的类*/
class FlyRiceFlour extends AbstractFryFlourTemplate{
@Override
protected void addFlour() {
Flour flour = new RiceFlour();
System.out.println("放入:"+flour.getName());
}
//客人说不要放辣椒
@Override
protected boolean isAddPepper() {
return false;
}
//客人还说也不要放醋
@Override
protected boolean isAddVinegar() {
return false;
}
}
测试一下:
public class Test {
public static void main(String[] args) {
AbstractFryFlourTemplate aft = new FlyNoodlesFlour();
aft.startFry();
System.out.println("*****************************");
AbstractFryFlourTemplate aft2 = new FlyRiceFlour();
aft2.startFry();
}
}
输出:*******************************************************************
刷锅...
放点油...
放入:挂面
各种花样百出的炒...
放点盐...
打开醋瓶
放点醋...
关好醋瓶
放点辣椒!
放到碗里去...
*****************************
刷锅...
放点油...
放入:米粉
默认慢慢的炒...
放点盐...
放到碗里去...
**************************************************************************
Ok,我们来分析一下上面的代码吧!AbstractFryFlourTemplate类定义了一个统一的炒面粉的模板,可以把它看做事一个算法骨架,一个框架; 我们看到,它完成一个算法的流程是固定的,但某些流程它无法自己完成,所以定义为抽象的,让子类去完成, 更有趣的是,它定义了两个钩子方法,钩子方法有默认值,也就是这个算法默认就会这么去做,当然它也允许子类去决定到底要不要这样做,比如要不要放醋; 这一点,我们可以说成是, 通过定义钩子方法,模板类可以让子类参与和改变算法的某些执行过程!
最后,我们来定义一下模板方法: 在一个方法中定义一个算法的骨架,但它允许将某些执行内容延迟到子类去执行; 通过钩子方法,它允许子类改变算法的执行过程!