《Head First設計模式》已經讀了不止一遍,但是始終沒有進行系統的進行總結。所以近期開始總結設計模式相關的知識,從模板方法模式開始,因為是一個我認為是最簡單的設計模式。(推薦視頻資源 "23個設計模式" ) 提出&解決問題 提出問題 實現製作咖啡功能 。且製作咖啡需要四個步驟 : 1. 燒水 ...
《Head First設計模式》已經讀了不止一遍,但是始終沒有進行系統的進行總結。所以近期開始總結設計模式相關的知識,從模板方法模式開始,因為是一個我認為是最簡單的設計模式。(推薦視頻資源23個設計模式)
提出&解決問題
提出問題
實現製作咖啡功能。且製作咖啡需要四個步驟 :
- 燒水
- 沖泡咖啡
- 倒入杯中
- 加糖
代碼實現
/**
* 一杯加糖咖啡
*
* @author Jann Lee
* @date 2019-07-14 18:37
*/
public class Coffee {
/**
* 製作一杯加糖咖啡
*/
public void prepareRecipe() {
boilWater();
steepTeaBag();
portInCup();
addLemon();
}
/**
* step1: 燒水
*/
private void boilWater() {
System.out.println("燒水...");
}
/**
* step2:沖泡咖啡
*/
private void steepTeaBag() {
System.out.println("沖泡咖啡...");
}
/**
* step3: 倒入杯中
*/
private void portInCup() {
System.out.println("倒入杯中...");
}
/**
* step4: 加糖
*/
private void addLemon() {
System.out.println("加糖...");
}
再次提出問題此時此刻我需要一杯檸檬茶呢?【燒水,沖泡茶包,倒入杯中,加檸檬】
這個問題當然很簡單,我們只需要如法炮製即可。
public class Tea {
/**
* 製作一杯檸檬茶
*/
public void prepareRecipe(){
boilWater();
brewCoffeeGrinds();
portInCup();
addSugarAndMilk();
}
/**
* step1: 燒水
*/
private void boilWater() {
System.out.println("燒水...");
}
/**
* step2:沖泡咖啡
*/
private void brewCoffeeGrinds() {
System.out.println("沖泡茶包...");
}
/**
* step3: 倒入杯中
*/
private void portInCup() {
System.out.println("倒入杯中...");
}
/**
* step4: 加檸檬
*/
private void addSugarAndMilk() {
System.out.println("加入檸檬片...");
}
}
思考
如果此時我們又需要一杯不加檸檬的茶,加奶的咖啡...,當然我們可以按照上面方式重新依次實現即可。但是如果你是一個有經驗的程式員,或者你學習過設計模式。你可能會發現以上功能實現的步驟/流程固定,當需求發生變化時,只有小部分步驟有所改變。
優化代碼
根據面向對象程式的特點,既抽象,封裝,繼承,多態。我們可以對代碼進行抽象,將公共代碼提取到基類。我們將咖啡和茶抽象成咖啡因飲料,將其中相同的兩步,燒水和倒入杯中再父類中實現,將沖泡和添加調料延遲到子類。
- 定義一個基類
public abstract class CafeineBeverage {
/**
* 製作一杯咖啡因飲料
*/
public void prepareRecipe() {
boilWater();
brew();
portInCup();
addCondiments();
}
/**
* step1: 燒水
*/
private void boilWater() {
System.out.println("燒水...");
}
/**
* step2:沖泡
*/
protected abstract void brew();
/**
* step3: 入杯中
*/
private void portInCup() {
System.out.println("倒入杯中...");
}
/**
* step4: 加調料
*/
protected abstract void addCondiments();
}
// 一杯加糖咖啡
public class CoffeeBeverage extends CafeineBeverage{
@Override
protected void brew() {
System.out.println("沖泡咖啡...");
}
@Override
protected void addCondiments() {
System.out.println("加糖...");
}
}
// 一杯檸檬茶
public class TeaBeverage extends CafeineBeverage {
@Override
protected void brew() {
System.out.println("沖泡茶包...");
}
@Override
protected void addCondiments() {
System.out.println("加檸檬...");
}
}
模板方法模式
如果按以上方式對代碼進行了優化,其實就實現了模板方法模式。一下是模板方法模式相關概念。
動機
- 在軟體構建過程中,對於某一項任務,它常常有穩定的整體操作結構,但是各個子步驟卻有很多改變的需求,或者由於固有的原因(比如框架與應用之間的關係)而無法和任務的整體結構同時實現
- 如何在確定穩定的操作結構的前提下,來靈活應對各個子步驟的變化或者晚期實現需求?
定義
定義一個操作中演算法的骨架(穩定),而將一些步驟延遲(變化)到子類。Template Method使得子類可以不改變(復用)一個演算法的結構,即可重新定義(override)該演算法的特定步驟。
要點總結
- Template Method是一種非常基礎性的設計模式,在面向對象系統中,有著大量的應用。他用最簡潔的機制(抽象類的多態,為很多應用框架提供了靈活的擴展點,是代碼復用方面最基本實現結構)
- 除了可以靈活應對子步驟的變化外,“不要調用我,讓我來調用你”的反向控制結構是Template Method的典型應用
- 在具體實現方面,被Template Method調用得虛方法可以有實現,也可以沒有實現(抽象方法),但一般推薦設置為protected方法
類圖: