我們先來談談 "模板" 的概念,什麼是模板? 很簡單嘛,直接上圖!
這就是一個模板!它定義了基本的樣式,整個文檔結構,內容大綱,而你要做的,就是再某些地方寫上自己的內容就可以了! 讓我們用代碼來舉一個模板方法的例子吧!
我們擬定一個炒米粉的過程(因爲我今天吃的是米粉),首先,要刷鍋,然後放油,放入粉條,翻炒, 放鹽,接下來可以選擇放醋,放辣椒,放蔥, 然後起鍋放入碗中, 自此整個流程完畢!
我們來分析一下這個過程:
通過畫圖,我們發現,其中某些步驟是完全固定的,那我們就可以思考,是不是可以有一個類,然後定義一個總的方法,這個方法就像是一個大的算法(把炒米粉看成是一個算法吧)骨架,或者說它就是一個模板,在這個方法中,它順序調用不同的方法(步驟) , 先調用刷鍋(),再放油()...接下來要解決放粉條的問題,怎麼辦,它自己不知道要放什麼粉條,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類定義了一個統一的炒麪粉的模板,可以把它看做事一個算法骨架,一個框架; 我們看到,它完成一個算法的流程是固定的,但某些流程它無法自己完成,所以定義爲抽象的,讓子類去完成, 更有趣的是,它定義了兩個鉤子方法,鉤子方法有默認值,也就是這個算法默認就會這麼去做,當然它也允許子類去決定到底要不要這樣做,比如要不要放醋; 這一點,我們可以說成是, 通過定義鉤子方法,模板類可以讓子類參與和改變算法的某些執行過程!
最後,我們來定義一下模板方法: 在一個方法中定義一個算法的骨架,但它允許將某些執行內容延遲到子類去執行; 通過鉤子方法,它允許子類改變算法的執行過程!