正文
一、定義
模板方法模式在一個方法中定義一個演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變演算法結構的情況下,重新定義演算法中的某些步驟。
要點:
- 模板方法定義了一個演算法的步驟,每個步驟都被一個方法所代表,而這幾個方法的具體實現可由子類提供。
- 模板方法可確保演算法的結構保持不變,同時由子類提供部分實現。
二、實現步驟
1、建立一個抽象類,並定義模板方法
模板方法一般宣告為 final,以免子類改變演算法的步驟。
抽象類中,可以宣告一些鉤子方法,子類視情況決定要不要覆蓋它們。鉤子的存在,可以讓子類有能力對演算法的不同點進行掛鉤,使得模板方法更具有彈性。
/**
* 抽象類
*/
public abstract class AbstractClass {
/**
* 模板方法
*/
public final void templateMethod() {
// 公共步驟,由抽象類實現
commonStep();
// 依賴於子類的步驟,由子類實現
step1();
step2();
step3();
// 鉤子方法,由子類決定要不要覆蓋
hook();
}
/**
* 步驟1
*/
public abstract void step1();
/**
* 步驟2
*/
public abstract void step2();
/**
* 步驟3
*/
public abstract void step3();
/**
* 公共步驟
*/
private void commonStep() {
System.out.println("I'm common step!");
}
/**
* 鉤子方法
*/
public void hook() {
// 可以空實現,也可以提供預設實現
}
}
2、建立具體子類,並提供演算法步驟的具體實現
(1)具體子類A
/**
* 具體子類A
*/
public class ConcreteClassA extends AbstractClass{
@Override
public void step1() {
System.out.println("I'm step A1!");
}
@Override
public void step2() {
System.out.println("I'm step A2!");
}
@Override
public void step3() {
System.out.println("I'm step A3!");
}
@Override
public void hook() {
System.out.println("I'm hook step A!");
}
}
(2)具體子類B
/**
* 具體子類B
*/
public class ConcreteClassB extends AbstractClass{
@Override
public void step1() {
System.out.println("I'm step B1!");
}
@Override
public void step2() {
System.out.println("I'm step B2!");
}
@Override
public void step3() {
System.out.println("I'm step B3!");
}
}
3、通過使用不同的子類,來確定具體的演算法步驟
public class Test {
public static void main(String[] args) {
AbstractClass classA = new ConcreteClassA();
classA.templateMethod();
System.out.println();
AbstractClass classB = new ConcreteClassB();
classB.templateMethod();
}
}
三、舉個例子
1、背景
《星巴茲咖啡師傅訓練手冊》規定了,準備星巴茲飲料時,必須精確地遵循下面的沖泡法:
星巴茲咖啡沖泡法:
- 把水煮沸
- 用沸水沖泡咖啡
- 把咖啡倒進杯子
- 加糖和牛奶
星巴茲茶沖泡法:
- 把水煮沸
- 用沸水浸泡茶葉
- 把茶倒進杯子
- 加檸檬
現在,讓我們扮演“程式碼師傅”,寫一些程式碼來建立咖啡和茶。
2、實現
(1)建立咖啡因飲料抽象類,並定義沖泡步驟的方法
/**
* 咖啡因飲料抽象類
*/
public abstract class CaffeineBeverage {
/**
* 準備沖泡法(模板方法)
*/
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
}
/**
* 沖泡(依賴於子類的步驟)
*/
abstract void brew();
/**
* 新增調料(依賴於子類的步驟)
*/
abstract void addCondiments();
/**
* 燒水(公共步驟)
*/
void boilWater() {
System.out.println("Boiling water");
}
/**
* 把飲料倒進杯子(公共步驟)
*/
void pourInCup() {
System.out.println("Pouring into cup");
}
/**
* 顧客是否想要新增調料(鉤子方法)
*/
boolean customerWantsCondiments() {
return true;
}
}
(2)建立具體的飲料,並提供具體沖泡步驟的實現
/**
* 咖啡
*/
public class Caffee extends CaffeineBeverage{
@Override
void brew() {
System.out.println("Dripping Coffee through filter");
}
@Override
void addCondiments() {
System.out.println("Adding Sugar and Milk");
}
@Override
boolean customerWantsCondiments() {
String answer = askCustomer();
if (answer.toLowerCase().startsWith("y")) {
return true;
} else {
return false;
}
}
/**
* 詢問顧客
*/
private String askCustomer() {
System.out.print("Would you like milk and suger with your coffee (y/n)?");
String answer = null;
Scanner scanner = new Scanner(System.in);
if (scanner.hasNext()) {
answer = scanner.nextLine();
}
scanner.close();
if (answer == null) {
return "no";
}
return answer;
}
}
/**
* 茶
*/
public class Tea extends CaffeineBeverage{
@Override
void brew() {
System.out.println("Steeping the tea");
}
@Override
void addCondiments() {
System.out.println("Adding Lemon");
}
}
(3)沖泡飲料
public class Test {
public static void main(String[] args) {
// 茶
System.out.println("\nMaking tea...");
CaffeineBeverage tea = new Tea();
tea.prepareRecipe();
// 咖啡
System.out.println("\nMaking coffee...");
CaffeineBeverage caffee = new Caffee();
caffee.prepareRecipe();
}
}