設計模式(十):模板方法模式


一、概述

  模板方法模式在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變算法結構的情況下,重新定義算法中的某些步驟。

二、結構類圖

  

 

三、解決問題

  模板方法就是提供一個算法框架,框架里面的步驟有些是父類已經定好的,有些需要子類自己實現。相當於要去辦一件事情,行動的流程已經定好了,但有些步驟需要自己去做,而有些步驟可能別人幫我們做了。就拿建網站來說,一般的程序是購買域名-->購買空間-->上傳網站-->備案-->審核,每個網站的創建必須經過這樣的固定程序,但除了審核不用建站者關心,其他的步驟都要建站者自己去完成。

四、應用實例

  現在我們很多家庭都有了豆漿機,豆漿的營養價值不用我多說了。制作豆漿的程序簡單點來說就是選材--->添加配料--->浸泡--->放到豆漿機打碎,通過添加不同的配料,可以制作出不同口味的豆漿,但是選材、浸泡和放到豆漿機打碎這幾個步驟對於制作每種口味的豆漿都是一樣的。

  1、創建抽象類

package templatemethod.pattern;

//豆漿類,抽象類
public abstract class SoyaMilk {
	//這是模板方法,用final修飾,不允許子類覆蓋。模板方法定義了制作豆漿的程序
	final void  prepareRecipe(){
		selectMaterial();
		addCondiments();
		soak();
		beat();
	}
	
	//選材方法,選擇黃豆
	void selectMaterial(){
		 System.out.println("第一步、選擇好了新鮮黃豆");
	}
	
	//可以添加不同的配料,在這里設置為抽象方法,子類必須實現
	abstract void addCondiments();
	
	//浸泡
	void soak(){
		System.out.println("第三步、黃豆和配料開始浸泡,大概需要5個小時");
	}
	
	//放到豆漿機打碎
	void beat(){
		System.out.println("第四步、黃豆的配料放到豆漿機打碎");
	}
	
}

  2、創建紅棗豆漿

package templatemethod.pattern;

//紅棗豆漿
public class ReddatesSoyaMilk extends SoyaMilk{
	//實現父類的添加配料方法
	@Override
	void addCondiments() {
		System.out.println("第二步、添加紅棗配料");
		
	}
	
}

  3、創建核桃豆漿

package templatemethod.pattern;

//核桃豆漿
public class NutSoyaMilk extends SoyaMilk{
	//實現父類的添加配料方法
	@Override
	void addCondiments() {
		System.out.println("第二步、添加核桃配料");		
	}

}

  4、測試制作豆漿

package templatemethod.pattern;

public class SoyaMilkTest {
	public static void main(String[] args){
		//制作紅棗豆漿
		System.out.println();
		System.out.println("-----制作紅棗豆漿步驟-------");
		SoyaMilk reddatesSoyaMilk = new ReddatesSoyaMilk();
		reddatesSoyaMilk.prepareRecipe();
		
		//制作核桃豆漿
		System.out.println();
		System.out.println("-----制作核桃豆漿步驟-------");
		SoyaMilk nutSoyaMilk = new NutSoyaMilk();
		nutSoyaMilk.prepareRecipe();
		
	}
}

  運行結果:

五、優缺點

  1、優點

  (1)、算法只存在於一個地方,也就是在父類中,容易修改。需要修改算法時,只要修改父類的模板方法或者已經實現的某些步驟,子類就會繼承這些修改。

  (2)、實現了最大化代碼復用。父類的模板方法和已實現的某些步驟會被子類繼承而直接使用。

  (3)、既統一了算法,也提供了很大的靈活性。父類的模板方法確保了算法的結構保持不變,同時由子類提供部分步驟的實現。

  (4)、提供了一個基本框架,容易擴展子類。模板方法有框架控制如何做事情,而由使用框架的人指定框架算法中每個步驟的細節。子類只要繼承父類,實現抽象方法,就可以使用父類的算法。

  2、缺點

  (1)、模板方法使用繼承方式復用代碼,如果要在基本算法里面增加一個步驟,而該步驟是抽象的話,每個子類都要修改代碼,實現這個步驟。

六、模板方法中的鈎子方法

  在模板方法模式的父類中,我們可以定義一個方法,它默認不做任何事,子類可以視情況要不要覆蓋它,該方法稱為“鈎子”。我們還是用上面做豆漿的例子來講解。

  1、創建有鈎子方法的父類

package templatemethod.pattern;

//豆漿類,抽象類
public abstract class SoyaMilkWithHook {
	//這是模板方法,用final修飾,不允許子類覆蓋。模板方法定義了制作豆漿的程序
	final void  prepareRecipe(){
		selectMaterial();
		//判斷是否添加配料
		if(customerWantsCondiments()){
			addCondiments();
		}		
		soak();
		beat();
	}
	
	//選材方法,選擇黃豆
	void selectMaterial(){
		 System.out.println("選擇好了新鮮黃豆");
	}
	
	//可以添加不同的配料,在這里設置為抽象方法,子類必須實現
	abstract void addCondiments();
	
	//浸泡
	void soak(){
		System.out.println("材料開始浸泡,大概需要5個小時");
	}
	
	//放到豆漿機打碎
	void beat(){
		System.out.println("材料放到豆漿機打碎");
	}
	
	//鈎子方法,是否添加配料
	boolean customerWantsCondiments(){
		return true;
	}
}

  2、創建純豆漿子類

package templatemethod.pattern;

//制作純豆漿,不添加任何配料
public class PureSoyaMilk extends SoyaMilkWithHook{

	@Override
	void addCondiments() {
		
	}
	
	//覆蓋鈎子方法,不添加配料
	@Override
	boolean customerWantsCondiments(){
		return false;
	}
}

  3、測試制作純豆漿

package templatemethod.pattern;

public class PureSoyaMilkTest {
	public static void main(String[] args){
		//制作純豆漿
		System.out.println();
		System.out.println("-----制作純豆漿步驟-------");
		SoyaMilkWithHook pureSoyaMilk = new PureSoyaMilk();
		pureSoyaMilk.prepareRecipe();
	}
}

  測試結果:

  鈎子方法的作用

  1、讓子類實現算法中的可選部分。算法中的某些步驟是可選的,子類可以做出決定是否需要這些步驟。

  2、如果鈎子對於子類的實現不重要時,子類可以對鈎子置之不理。

  3、鈎子可以讓子類能夠有機會對模板方法中某些即將發生的(或剛剛發生的)步驟作出反應。可以在鈎子中實現我們對於某個步驟執行需要作出的動作,模板方法的某個步驟執行時,調用鈎子。

七、模板方法與策略的比較

  1、相同點

  (1)、都封裝了算法。

  2、不同點

  (1)、策略使用組合方式實現算法,客戶可以自由選擇算法;模板方法使用繼承方式獲得算法,確保算法的基本結構不變。

  (2)、策略代碼復用程度低,每個算法都要對應一個類;模板方法代碼復用高,子類可以繼承父類的代碼。  

  (3)、策略依賴少,自己實現整個算法;模板方法依賴高,子類依賴父類中的方法的實現。

八、使用場景

  1、當要完成一件事情,它有固定的程序流程,但某些步驟是自定義的,使用模板方法。

  2、當需要創建框架時,在超類中提供一個基礎的方法,達到代碼的復用,並允許子類指定行為。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM