設計模式之模板設計模式
模板設計模式—基於抽象類的,核心是封裝演算法
- 模板方法定義了一個演算法的步驟,並允許子類為一個或多個步驟提供具體實現
- 模板(模板方法)模式(Servlet、AQS)
- 在一個方法中定義一個演算法的骨架,並將一些具體步驟延遲到子類中實現。
- 模板模式使得子類可以在不改變演算法結構的基礎上,重新具體定義演算法中的某些步驟
講模板設計模式之前,我們用程式碼來實現咖啡和茶製作的類:
class Coffee {
/*
* 咖啡沖泡法(演算法)
*/
void prepareRecipe() {
boilWater();
brewCoffeeGrings();
pourInCup();
addSugarAndMilk();
}
public void boilWater() {
System.out.println("將水煮沸");
}
public void brewCoffeeGrings() {
System.out.println("沖泡咖啡");
}
public void pourInCup() {
System.out.println("把咖啡倒進杯子中");
}
public void addSugarAndMilk() {
System.out.println("加糖和牛奶");
}
}
class Tea {
/*
* 沖泡茶法(演算法)
*/
void prepareRecipe() {
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
public void boilWater() {
System.out.println("將水煮沸");
}
public void steepTeaBag() {
System.out.println("浸泡茶葉");
}
public void pourInCup() {
System.out.println("把茶倒進杯子中");
}
public void addLemon() {
System.out.println("加檸檬");
}
}
class Test {
public static void main(String[] agrs) {
Coffee coffee = new Coffee();
Tea tea = new Tea();
coffee.prepareRecipe();
tea.prepareRecipe();
}
}
我們在這兩個類中發現了重複程式碼,因此我們需要重新理一下我們的設計。
- 既然茶和咖啡是如此的相似,因此我們應該將共同的部分抽取出來,放進一個基類中。
- 從沖泡法入手。觀察咖啡和茶的沖泡法我們會發現,兩種沖泡法都採用了相同的演算法:
- 將水煮沸
- 用熱水泡飲料
- 把飲料倒進杯子
- 在飲料內加入適當的調料
實際上,浸泡(steep)和沖泡(brew)差異並不大。因此我們給它一個新的方法名稱brew(),這樣我們無論沖泡的是何種飲 料都可以使用這個方法。同樣的,加糖、牛奶還是檸檬也很相似,都是在飲料中加入其它調料,因此我們也給它一 個通用名稱addCondiments()。
/**
* 咖啡因飲料是一個抽象類
**/
abstract class CaffeineBeverage {
/**
* 現在用同一個prepareRecipe()方法處理茶和咖啡。
* 宣告為final的原因是我們不希望子類覆蓋這個方法!
**/
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
/**
* 咖啡和茶處理這些方法不同,因此這兩個方法必須被宣告為抽象,留給子類實現
**/
abstract void brew();
abstract void addCondiments();
void boilWater() {
System.out.println("將水煮沸");
}
void pourInCup() {
System.out.println("把飲料倒進杯子中");
}
}
class Coffee extends CaffeineBeverage {
public void brew() {
System.out.println("沖泡咖啡");
}
public void addCondiments() {
System.out.println("加糖和牛奶");
}
}
class Tea extends CaffeineBeverage {
public void brew() {
System.out.println("浸泡茶葉");
}
public void addCondiments() {
System.out.println("加檸檬");
}
}
class Test {
public static void main(String[] agrs) {
CaffeineBeverage coffee = new Coffee();
CaffeineBeverage tea = new Tea();
coffee.prepareRecipe();
tea.prepareRecipe();
}
}
模板方法定義了一個演算法的步驟,並允許子類為一個或者多個步驟提供具體實現
在模板設計模式下還有一種鉤子用法
鉤子方法是一類"預設不做事的方法" ,子類可以視情況決定要不要覆蓋它們。
比如說,顧客點杯咖啡時,可以選擇加不加牛奶或者糖!!!
import java.util.Scanner;
/**
-
咖啡因飲料是一個抽象類
/
abstract class CaffeineBeverage {
/
- 現在用同一個prepareRecipe()方法處理茶和咖啡。
- 宣告為final的原因是我們不希望子類覆蓋這個方法!
/
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
if(customerWantsCondiments())
addCondiments();
}
/
- 咖啡和茶處理這些方法不同,因此這兩個方法必須被宣告為抽象,留給子類實現
**/
abstract void brew();
abstract void addCondiments();
void boilWater() {
System.out.println("將水煮沸");
}
void pourInCup() {
System.out.println("把飲料倒進杯子中");
}
boolean customerWantsCondiments() {
return true;
}
}
class Coffee extends CaffeineBeverage {
public void brew() {
System.out.println("沖泡咖啡");
}
public void addCondiments() {
System.out.println("加糖和牛奶");
}
/**
* 子類覆寫了鉤子函式,實現自定義功能
**/
boolean customerWantsCondiments() {
String result = getUserInput();
if(result.equals("y"))
return true;
else
return false;
}
private String getUserInput() {
System.out.println("您想要在咖啡中加入牛奶或糖嗎(y/n)?");
Scanner scanner = new Scanner(System.in);
String str = scanner.nextLine();
return str;
}
}
class Tea extends CaffeineBeverage {
public void brew() {
System.out.println("浸泡茶葉");
}
public void addCondiments() {
System.out.println("加檸檬");
}
}
class Test {
public static void main(String[] agrs) {
CaffeineBeverage coffee = new Coffee();
CaffeineBeverage tea = new Tea();
coffee.prepareRecipe();
tea.prepareRecipe();
}
}