日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

《Head First设计模式》第八章笔记-模板方法模式

發布時間:2023/12/13 asp.net 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《Head First设计模式》第八章笔记-模板方法模式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

模板方法模式

之前所學習的模式都是圍繞著封裝進行,如對象創建、方法調用、復雜接口的封裝等,這次的模板方法模式將深入封裝算法塊,好讓子類可以在任何時候都將自己掛接進運算里。

模板方法定義:模板方法模式在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變算法結構的情況下,重新定義算法中的某些步驟。

模板方法就是一個固定步驟的“算法”骨架方法。這個算法的可變部分通過繼承,在子類中重載實現。這樣就可以在算法骨架不變的情況下,算法細節步驟根據不同的需求進行適應的改變。

例題:茶飲店的飲品沖泡程序(泡茶與泡咖啡)

1

2

3

4

5

6

泡茶:???????????????????????????????????????????? |??????????? 咖啡:

??????????????????????????????????????????????????|

1. 煮沸水????????????????????????????????????????? |??????????????1.煮沸水?????????

2. 加入茶葉沖泡???????????????????????????????????? |??????????????2.加入咖啡粉沖泡

3. 根據需求加入調料(如蜂蜜、檸檬)?????????????????? |??????????????3.根據需求加入調料(如牛奶、糖)

4. 將泡好的茶水倒入杯子????????????????????????????? |??????????????4.將泡好的咖啡倒入杯子

我們發現兩者的步驟非常相似,僅有部分細節不一:如泡茶沖的是茶葉,加的是蜂蜜;泡咖啡加的是牛奶其實泡茶和泡咖啡的過程就是一個固定骨架步驟的“算法”,我們可以抽象為:

  • 煮沸水
  • 沖泡
  • 根據需求加入調料
  • 將泡好的飲料倒入杯子
  • 斜體部分為算法中不一樣的部分,如何解決?下面,我們用“模板方法模式”來解決這種不一致。

    首先,定義一個含有固定骨架“模板方法”的咖啡因飲料抽象類:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    /**

    ?*咖啡因飲料

    ?*/

    public abstract class CaffeineBeverage {

    ????/**

    ?????*模板方法,準備飲料

    ?????*/

    ????public final void prepareRecipe(){

    ????????boilWater();

    ????????brew();

    ????????//用于模板方法的算法中可選部分的控制

    ????????if(customerWantsCondiment())

    ????????????addCondiment();

    ????????pourInCup();

    ????}

    ?

    ????/**

    ?????*煮沸水

    ?????*/

    ????public void boilWater() {

    ????????System.out.println("煮沸水");

    ????}

    ?

    ????/**

    ????????*沖泡

    ?????*/

    ????public abstract void brew();

    ?

    ????/**

    ?????*增加調味劑

    ?????*/

    ????public abstract void addCondiment();

    ?

    ????/**

    ??????*將飲料倒入杯子

    ?????*/

    ????public void pourInCup() {

    ????????System.out.println("將飲料倒入杯中");

    ????}

    ?

    ????/**

    ?????*“鉤子”方法。顧客決定是否加調料

    ?????*/

    ????public Boolean customerWantsCondiment(){

    ????????return true;

    ????}

    }

    這時,準備飲料的四個固定步驟我們都寫在模板方法prepareRecipe()里了。這個算法步驟是不可更改的,所以我們給這個模板方法加了final關鍵字。
    然后,根據茶和咖啡在算法步驟上的不同,我們設計兩個類,繼承抽象方法,分別重載模板方法中的步驟,從而實現茶和咖啡在算法步驟中各自的不同:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    public class Tea?extends CaffeineBeverage{

    ????@Override

    ????public void brew() {

    ????????System.out.println("浸泡茶葉");

    ????}

    ?

    ????@Override

    ????public void addCondiment() {

    ????????System.out.println("添加蜂蜜");

    ????}

    }

    ?

    public class Coffee?extends CaffeineBeverage{

    ????@Override

    ????public void brew() {

    ????????System.out.println("沖泡咖啡粉");

    ????}

    ?

    ????@Override

    ????public void addCondiment() {

    ????????System.out.println("添加糖和牛奶");

    ????}

    }

    測試:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    public class Main {

    ????public static void main(String[] args) {

    ????????CaffeineBeverage tea =?new Tea();

    ????????tea.prepareRecipe();

    ????????System.out.println("===============");

    ????????CaffeineBeverage coffee =?new Coffee();

    ????????coffee.prepareRecipe();

    ????}

    }

    /**輸出:

    ?煮沸水

    ?浸泡茶葉

    ?添加蜂蜜

    ?將飲料倒入杯中

    ?===============

    ?煮沸水

    ?沖泡咖啡粉

    ?添加糖和牛奶

    ?將飲料倒入杯中

    */

    可以看到,通過繼承,模板方法在茶和咖啡中的實現有了差別。
    模板方法將算法定義成一組步驟,其中的任何步驟都可以是抽象的,由子類負責實現。這樣可以確保算法的結構保持不變,同時由子類提供部分的實現。
    所以,模板方法就是定義了一個算法的步驟,并允許子類為一個或多個步驟提供實現。
    最后,我們給出模板方法的類圖:

    看上去模板方法似乎就這樣結束了,然而,在上面定義的抽象類中還有一個“鉤子(hook)”方法,
    什么是“鉤子”方法呢?我們來看一下定義:
    鉤子是一種被聲明在抽象類中的方法,但只有空的或者默認的實現。鉤子的存在,可以讓子類有能力對算法的不同點進行掛鉤。要不要掛鉤,由子類自行決定。

    在上面的代碼中,我們寫了一個鉤子來決定是否加調料

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    /**

    ?* “鉤子”方法。顧客決定是否加調料

    ?*/

    public Boolean customerWantsCondiment(){

    ????return true;

    }

    /**

    ?* 模板方法,準備飲料

    ?*/

    public final void prepareRecipe(){

    ????boilWater();

    ????brew();

    ????//用于模板方法的算法中可選部分的控制

    ????if(customerWantsCondiment())

    ????????addCondiment();

    ????pourInCup();

    }

    現在,我們用一個子類來掛鉤:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    public class Tea?extends CaffeineBeverage{

    ????@Override

    ????public void brew() {

    ????????System.out.println("浸泡茶葉");

    ????}

    ?

    ????@Override

    ????public void addCondiment() {

    ???????????System.out.println("添加蜂蜜");

    ????}

    ?

    ????//覆蓋父類的“鉤子”方法,更改算法中的可選部分

    ????@Override

    ????public Boolean customerWantsCondiment(){

    ????????//詢問顧客是否需要調料

    ????????String answer = askCustomerNeedCondiment();

    ????????if("y".equals(answer))

    ????????????return true;

    ????????else

    ????????????return false;

    ????}

    ?

    ????private String askCustomerNeedCondiment() {

    ????????String answer =?null;

    ????????System.out.println("請問您要不要加蜂蜜?請回答y或n");

    ????????BufferedReader in =?new BufferedReader(new InputStreamReader(System.in));

    ????????try {

    ????????????answer = in.readLine();

    ????????}?catch (IOException e) {

    ????????????e.printStackTrace();

    ????????}

    ????????return answer;

    ????}

    }

    這時,我們準備茶水時就能根據顧客的回答而安排需要加調料這一步驟了。子類通過覆蓋鉤子方法,實現了算法中的可選部分。

    模板方法模式中還使用到了一個新的設計原則:好萊塢原則
    好萊塢原則:別調用(打電話給)我們,我們會調用(打電話給)你

    好萊塢原則可以給我們一種防止“依賴腐敗”的方法。當高層組件依賴低層組件,而低層組件又依賴高層組件,而高層組件又依賴邊側組件,而邊側組件又依賴低層組件時,依賴腐敗就發生了。在這種情況下,沒有人可以輕易地搞懂系統是如何設計的。
    在好萊塢原則之下,我們允許低層組件將自己掛鉤到系統上,但是高層組件會決定什么時候和怎樣使用這些低層組件。換句話說,高層組件對待低層組件的方式是“別調用我們,我們會調用你”。

    好萊塢原則就是確保不會出現高層組件依賴底層組件、底層組件又依賴高層組件的“依賴腐敗”。只有高層組件會決定什么時候和怎樣使用底層組件,而底層組件不會調用高層組件


    在模板方法模式中,算法的實現會調用到具體子類的某個方法,也就是高層組件依賴于底層組件。具體子類不會調用父類中的方法,不會形成底層組件依賴高層組件的環狀依賴:

    采用好萊塢原則的設計模式還有:工廠方法(可以看作特殊的模板方法),觀察者、裝飾者...

    好萊塢原則和依賴倒置原則的關系:依賴倒置原則是盡量避免使用具體類,多使用抽象。

    策略模式與模板方法模式

    策略模式和模板方法模式很像,都是針對算法改變的情況的設計模式。以下是它們的區別:

    • 策略模式是采用的組合來實現算法的變化,這樣的設計更加靈活,依賴性程度低;
    • 模板方法模式采用的繼承來實現算法中的變化部分,這樣的設計對算法有更多的控制權,且代碼的重復會少一些,但由于算法依賴于父類,所以依賴程度高。

    Java API中的模板方法

    Java中較常見的模板方法模式的應用:

  • java.io的InputStream類有一個read()方法,是由子類實現的,而這個方法又會被read(byte b[], int off, int len)模板方法使用。
  • Swing的JFrame繼承了一個paint()方法。在默認狀態下,paint()是不做事情的,因為它是一個“鉤子”。通過覆蓋paint(),可以將自己的代碼插入JFrame的算法中,顯示出想要的畫面。
  • applet是一個能在網頁上面執行的小程序。任何applet必須繼承自Applet類,而Applet類中提供了好些鉤子。
  • Java數組排序方法,如Arrays.sort(Object[]);Object對象實現了Comparable接口,排序通過Comparable接口中的compareTo()實現。
  • 注意,這個排序的例子表面看上去好像與模板方法模式無關(沒有用到繼承),但實質仍是通過子類提供算法步驟的實現來實現了算法的變化。雖然采用了組合,但思想仍是模板方法模式的思想。

    總結

    模板方法模式:在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變算法的結構情況下,重新定義算法中的某些步驟。

    • 模板方法定義了算法的步驟,把這些步驟實現延遲到子類。
    • 模板方法為我們提供了一種代碼復用的重要技巧。
    • 模板方法的抽象類可以定義具體的方法、抽象方法和鉤子方法。
    • 抽象方法由子類實現。
    • 鉤子是一種方法,它在抽象類中不做事,或者只做默認的事,子類可以選擇要不要覆蓋它。
    • 好萊塢原則告訴我們將決策權放在高層模板中,以便決定如何及何時調用底層模塊。
    • 策略模式和模板方法模式都封裝算法,一個是用組合,一個是用繼承。
    • 工廠方法是模板方法的一種特殊版本

    總結

    以上是生活随笔為你收集整理的《Head First设计模式》第八章笔记-模板方法模式的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。