目錄
- 模版方法模式
- 模版方法模式結構
- 模版方法模式適合應用場景
- 模版方法模式優缺點
- 練手題目
- 題目描述
- 輸入描述
- 輸出描述
- 題解
模版方法模式
模板方法模式是一種行為設計模式, 它在超類中定義了一個算法的框架, 允許子類在不修改結構的情況下重寫算法的特定步驟。
類比真實世界中建造大量房屋。 標準房屋建造方案中可提供幾個擴展點, 允許潛在房屋業主調整成品房屋的部分細節。
每個建造步驟 (例如打地基、 建造框架、 建造墻壁和安裝水電管線等) 都能進行微調, 這使得成品房屋會略有不同。
模版方法模式結構
-
抽象類 (Abstract-Class) 會聲明作為算法步驟的方法, 以及依次調用它們的實際模板方法。 算法步驟可以被聲明為
抽象
類型, 也可以提供一些默認實現。 -
具體類 (Concrete-Class) 可以重寫所有步驟, 但不能重寫模板方法自身。
一般,模版方法都加上final關鍵字,不允許被覆寫。
通用代碼結構
//抽象類定義了一個模板方法,其中通常會包含某個由抽象原語操作調用組成的算法框架。
public abstract class AbstractClass{//基本算法步驟protected abstract void step1();protected abstract void step2();//模版方法final public void templateMethod(){//算法基本邏輯this.step1();this.step2();...}
}// 具體類必須實現基類中的所有抽象操作,但是它們不能重寫模板方法自身。
public class ConcreteClass1 extends AbstractClass{//實現基本方法protected abstract void step1(){...};protected abstract void step2(){....};
}public class ConcreteClass2 extends AbstractClass{//實現基本方法protected abstract void step1(){...};protected abstract void step2(){....};
}//客戶端
public class Client{public static void main(String[] args){AbstractClass class1 = new ConcreteClass1();AbstractClass class2 = new ConcreteClass2();class1.templateMethod();class2.templateMethod();}
}
模版方法模式適合應用場景
-
當你只希望客戶端擴展某個特定算法步驟,而不是整個算法或其結構時,可使用模板方法模式。
-
當多個類的算法除一些細微不同之外幾乎完全一樣時,你可使用該模式。但其后果就是,只要算法發生變化,你就可能需要修改所有的類。
**識別方法:**模版方法可以通過行為方法來識別,該方法已有一個在基類中定義的 “默認” 行為。
模版方法模式優缺點
模版方法模式的優點
-
你可僅允許客戶端重寫一個大型算法中的特定部分, 使得算法其他部分修改對其所造成的影響減小。
-
你可將重復代碼提取到一個超類中。
模版方法模式的缺點
-
部分客戶端可能會受到算法框架的限制。
-
通過子類抑制默認步驟實現可能會導致違反里氏替換原則。
-
模板方法中的步驟越多, 其維護工作就可能會越困難。
練手題目
題目描述
小明喜歡品嘗不同類型的咖啡,她發現每種咖啡的制作過程有一些相同的步驟,他決定設計一個簡單的咖啡制作系統,使用模板方法模式定義咖啡的制作過程。系統支持兩種咖啡類型:美式咖啡(American Coffee)和拿鐵(Latte)。
咖啡制作過程包括以下步驟:
研磨咖啡豆 Grinding coffee beans
沖泡咖啡 Brewing coffee
添加調料 Adding condiments
其中,美式咖啡和拿鐵的調料添加方式略有不同, 拿鐵在添加調料時需要添加牛奶Adding milk
輸入描述
多行輸入,每行包含一個數字,表示咖啡的選擇(1 表示美式咖啡,2 表示拿鐵)。
輸出描述
根據每行輸入,輸出制作咖啡的過程,包括咖啡類型和各個制作步驟,末尾有一個空行。
題解
模版方法實現。
import java.util.Scanner;// 抽象類,定義咖啡制作的基本步驟
abstract class CoffeeModel {private String coffeeName;// 構造函數,接受咖啡名稱參數public CoffeeModel(String coffeeName) {this.coffeeName = coffeeName;}protected abstract void grind();protected abstract void brew();protected abstract void addCondiments();// 添加其他調料可使用該類public void addThings(){};// 模板方法,定義咖啡制作的流程public final void createCoffeeTemplate() {System.out.println("Making " + coffeeName + ":");grind();brew();//根據情況,是否調用添加更多調料if (isAddThings()) {addThings(); }addCondiments();System.out.println();}// 默認不添加其他調料。如牛奶等public boolean isAddThings() {return false;}
}//美式咖啡類實現
class CreateAmericanCoffee extends CoffeeModel {public CreateAmericanCoffee() {super("American Coffee");}@Overrideprotected void grind() {System.out.println("Grinding coffee beans");}@Overrideprotected void brew() {System.out.println("Brewing coffee");}@Overrideprotected void addCondiments() {System.out.println("Adding condiments");}// 美式咖啡默認不添加其他調料,如牛奶等@Overridepublic boolean isAddThings() {return false; }
}//拿鐵類實現
class CreateLatte extends CoffeeModel {private boolean addThingsFlag = true;public CreateLatte() {super("Latte");}@Overrideprotected void grind() {System.out.println("Grinding coffee beans");}@Overrideprotected void brew() {System.out.println("Brewing coffee");}@Overrideprotected void addCondiments() {System.out.println("Adding condiments");}//需要添加調料,牛奶@Overridepublic void addThings(){System.out.println("Adding milk");}// 拿鐵默認添加牛奶@Overridepublic boolean isAddThings() {return this.addThingsFlag; }// 外部調用以改變是否添加牛奶的狀態,鉤子函數public void setAddThingsFlag(boolean flag) {this.addThingsFlag = flag;}
}public class Main {public static void main(String[] args) {try (Scanner scanner = new Scanner(System.in)) {while (scanner.hasNextInt()) {int input = scanner.nextInt();CoffeeModel coffee;switch (input) {case 1:coffee = new CreateAmericanCoffee();break;case 2:coffee = new CreateLatte();break;default:System.out.println("無效選擇,請輸入1或2");continue;}coffee.createCoffeeTemplate();}}}
}