1. 程式人生 > >Java學習——模板設計模式——抽象類的實際應用

Java學習——模板設計模式——抽象類的實際應用

設計模式的精髓:解耦。而模板設計模式是通過第三方進行解耦

什麼是內聚、解耦大家可以看一下博主 小異常 的博文:https://blog.csdn.net/sun8112133/article/details/81040275

模板設計模式:(基於抽象類)在一個方法中定義一個演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類再不改變演算法的前提下,重新定義演算法中的某些步驟。

通俗點的理解就是 :完成一件事情,有固定的數個步驟,但是每個步驟根據物件的不同,而實現細節不同;就可以在父類中定義一個完成該事情的總方法,按照完成事件需要的步驟去呼叫其每個步驟的實現方法。每個步驟的具體實現,由子類完成。

一個經典的模板設計模式的例子

星巴克咖啡沖泡法

1. 將水煮沸
2. 用沸水沖泡咖啡
3. 將咖啡倒進杯子
4. 加糖和牛奶

星巴克茶沖泡法

1. 將水煮沸
2. 用沸水浸泡茶葉
3. 把茶倒進杯子
4. 加檸檬

用程式碼簡單實現就是

class Coffee{
    void prepareRecipe(){
        boilWater();
        brewCoffeeGrings();
        pourInCap();
        addSugarAndMilk();
    }
    public void boilWater(){
        System.out.println("將水煮沸");
    }
    public void brewCoffeeGrings(){
        System.out.println("沖泡咖啡");
    }
    public void pourInCap(){
        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[] args) {
        Coffee coffee = new Coffee();
        coffee.prepareRecipe();
        Tea tea = new Tea();
        tea.prepareRecipe();
    }
}

讓我們先從沖泡法入手。觀察咖啡和茶的沖泡法我們會發現,兩種沖泡法都採用了相同的演算法:
1. 將水煮沸
2. 用熱水泡飲料
3. 把飲料倒進杯子
4. 在飲料內加入適當的調料

實際上,浸泡(steep)和沖泡(brew)差異並不大。因此我們給它一個新的方法名稱brew(),這樣我們無論沖泡的是何種飲
料都可以使用這個方法。同樣的,加糖、牛奶還是檸檬也很相似,都是在飲料中加入其它調料,因此我們也給它一
個通用名稱addCondiments()。我們可以將這些重複的操作寫在一個抽象類中,不同的地方寫抽象方法,讓實現的操作延遲到子類去具體完成。

/**
* 咖啡因飲料是一個抽象類
*/
abstract class  CaffeineBeverage{
    /**
    * 宣告為final的原因是我們不希望子類覆蓋這個方法!
    */
    final void prepareRecipe(){
        boilWater();
        brew();
        pourInCap();
        addCondiments();
    }
    /**
    * 咖啡和茶處理這些方法不同,因此這兩個方法必須被宣告為抽象,留給子類實現
    */
    abstract void brew();
    abstract void addCondiments();
    public void boilWater(){
        System.out.println("將水煮沸");
    }
    public void pourInCap(){
        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[] args) {
        CaffeineBeverage coffee = new Coffee();
        coffee.prepareRecipe();
        CaffeineBeverage tea = new Tea();
        tea.prepareRecipe();
    }
}

此時的類圖:

我們剛剛實現的就是模板設計模式,它包含了實際的"模板方法"

模板方法定義了一個演算法的步驟,並允許子類為一個或者多個步驟提供具體實現。

不好的茶或咖啡實現                                                                      模板方法提供的咖啡因飲料
Coffee或Tea主導一切,控制演算法                                                  由超類主導一切,它擁有演算法,並且保護這個演算法
Coffee與Tea之間存在重複程式碼                                                      有超類的存在,因此可以將程式碼複用最大化
對於演算法所做的程式碼改變,需要開啟各個子類修改很多地方         演算法只存在一個地方,容易修改
彈性差,新種類的飲料加入需要做很多工作                                   彈性高,新飲料的加入只需要實現自己的沖泡和加料方法即可
演算法的知識和它的實現分散在許多類中                                          超類專注於演算法本身,而由子類提供完整的實現。

一個完整的模板模式超類的定義:

/**
* 基類宣告為抽象類的原因是
* 其子類必須實現其操作
*/
abstract class AbstractClass {
    /**
    * 模板方法,被宣告為final以免子類改變這個演算法的順序
    */
    final void templateMethod() {
    }
    /**
    * 具體操作延遲到子類中實現
    */
    abstract void primitiveOperation1();
    abstract void primitiveOperation2();
    /**
    * 具體操作且共用的方法定義在超類中,可以被模板方法或子類直接使用
    */
    final void concreteOperation() {
    // 實現
    }
    /**
    * 鉤子方法是一類"預設不做事的方法"
    * 子類可以視情況決定要不要覆蓋它們。
    */
    void hook() {
    // 鉤子方法
    }
}

什麼是鉤子方法:鉤子方法在我理解就是當我們需要用法從其他地方才能獲取的值時,我們需要一個鉤子把它鉤過來,在下面的程式碼裡就是我們從主類鉤過來了使用者的輸入,再在其他類中根據這個輸入做具體的操作,這樣使我們的程式更加靈活。

import java.util.Scanner;
abstract class  CaffeineBeverage{

    final void prepareRecipe(){
        boilWater();
        brew();
        pourInCap();
        if(customerWantsCondiments()){
            addCondiments();
        }
    }
    abstract void brew();
    abstract void addCondiments();
    public void boilWater(){
        System.out.println("將水煮沸");
    }
    public void pourInCap(){
        System.out.println("將飲料倒進杯子");
    }
    /**
    * 鉤子方法
    * 超類中通常是預設實現
    * 子類可以選擇性的覆寫此方法
    * @return
    */
    public boolean customerWantsCondiments(){
        return true;
    }
}
class Coffee extends CaffeineBeverage{
    public void brew(){
        System.out.println("沖泡咖啡");
    }
    public void addCondiments(){
        System.out.println("加入糖和牛奶");
    }
    /**
    * 子類覆寫了鉤子函式,實現自定義功能
    * @return
    */
    public boolean customerWantsCondiments(){
        String str = getUserInput();
        if(str.equals("y"))
            return true;
        else
            return false;
    }
    private String getUserInput() {
        String answer = null;
        System.out.println("您想要在咖啡中加入牛奶或糖嗎 (y/n)?");
        Scanner scanner = new Scanner(System.in);
        answer = scanner.nextLine();
        return answer;
    }
}
class Test{
    public static void main(String[] args) {
        CaffeineBeverage coffee = new Coffee();
        coffee.prepareRecipe();
    }
}