<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ## 一、定義 >模板,顧名思義,它是一個固定化、標準化的東西。 模板方法模式是一種行為設計模式, 它在超類中定義了一個算法的框架, 允許子類在不修改結構的情況下重寫算法的特定步驟。 ## 二、場景問題 > 程序員不愿多扯,上來先干兩行代碼 網上模板方法的場景示例特別多,個人感覺還是《Head First 設計模式》中的例子比較好。 假設我們是一家飲品店的師傅,起碼需要以下兩個手藝 ![](https://img.kancloud.cn/cd/66/cd66a58480f8217c6acf38b5874259f7_1080x348.png) > 真簡單哈,這么看,步驟大同小異,我的第一反應就是寫個業務接口,不同的飲品實現其中的方法就行,像這樣 ![](https://img.kancloud.cn/e9/a8/e9a83d23615551551c9dd4057adf17b5_1080x526.png) > 畫完類圖,猛地發現,第一步和第三步沒什么差別,而且做飲品是個流程式的工作,我希望使用時,直接調用一個方法,就去執行對應的制作步驟。 靈機一動,不用接口了,用一個抽象父類,把步驟方法放在一個大的流程方法`makingDrinks()`中,且第一步和第三步,完全一樣,沒必要在子類實現,改進如下 ![](https://img.kancloud.cn/54/72/54728310c6aa22c5c1e71a060f8a609f_1080x528.png) > 再看下我們的設計,感覺還不錯,現在用同一個`makingDrinks()`方法來處理咖啡和茶的制作,而且我們不希望子類覆蓋這個方法,所以可以申明為 final,不同的制作步驟,我們希望子類來提供,必須在父類申明為抽象方法,而第一步和第三步我們不希望子類重寫,所以我們聲明為非抽象方法 ``` publicabstractclass?Drinks?{ void?boilWater()?{ ????????System.out.println("將水煮沸"); ????} abstract?void?brew(); void?pourInCup()?{ ????????System.out.println("倒入杯子"); ????} abstract?void?addCondiments(); public?final?void?makingDrinks()?{ //熱水 ????????boilWater(); //沖泡 ????????brew(); //倒進杯子 ????????pourInCup(); //加料 ????????addCondiments(); ????} } ``` > 接著,我們分別處理咖啡和茶,這兩個類只需要繼承父類,重寫其中抽象方法即可(實現各自的沖泡和添加調料) ``` publicclass?Tea?extends?Drinks?{ @Override void?brew()?{ ????????System.out.println("沖茶葉"); ????} @Override void?addCondiments()?{ ????????System.out.println("加檸檬片"); ????} } ``` ``` publicclass?Coffee?extends?Drinks?{ @Override void?brew()?{ ????????System.out.println("沖咖啡粉"); ????} @Override void?addCondiments()?{ ????????System.out.println("加奶加糖"); ????} } ``` ``` public?static?void?main(String\[\]?args)?{ ????Drinks?coffee?=?new?Coffee(); ????coffee.makingDrinks(); ????System.out.println(); ????Drinks?tea?=?new?Tea(); ????tea.makingDrinks(); } ``` > 這就是模板方法模式,我們的`makingDrinks()`就是模板方法。我們可以看到相同的步驟`boilWater()`和`pourInCup()`只在父類中進行即可,不同的步驟放在子類實現。 ## 三、認識模板方法 在閻宏博士的《JAVA與模式》一書中開頭是這樣描述模板方法(Template Method)模式的: > 模板方法模式是類的行為模式。準備一個抽象類,將部分邏輯以具體方法以及具體構造函數的形式實現,然后聲明一些抽象方法來迫使子類實現剩余的邏輯。不同的子類可以以不同的方式實現這些抽象方法,從而對剩余的邏輯有不同的實現。這就是模板方法模式的用意。 寫代碼的一個很重要的思考點就是“變與不變”,程序中哪些功能是可變的,哪些功能是不變的,我們可以把不變的部分抽象出來,進行公共的實現,把變化的部分分離出來,用接口來封裝隔離,或用抽象類約束子類行為。模板方法就很好的體現了這一點。 模板方法定義了一個算法的步驟,并允許子類為一個或多個步驟提供實現。 模板方法模式是所有模式中最為常見的幾個模式之一,是基于繼承的代碼復用的基本技術,我們再看下類圖 ![](https://img.kancloud.cn/07/e0/07e05268125626056752aef964e0631b_1080x488.png) 模板方法模式就是用來創建一個算法的模板,這個模板就是方法,該方法將算法定義成一組步驟,其中的任意步驟都可能是抽象的,由子類負責實現。這樣可以確保算法的結構保持不變,同時由子類提供部分實現。 再回顧下我們制作咖啡和茶的例子,有些顧客要不希望咖啡加糖或者不希望茶里加檸檬,我們要改造下模板方法,在加相應的調料之前,問下顧客 ``` publicabstractclass?Drinks?{ void?boilWater()?{ ????????System.out.println("將水煮沸"); ????} abstract?void?brew(); void?pourInCup()?{ ????????System.out.println("倒入杯子"); ????} abstract?void?addCondiments(); public?final?void?makingDrinks()?{ ????????boilWater(); ????????brew(); ????????pourInCup(); //如果顧客需要,才加料 if?(customerLike())?{ ????????????addCondiments(); ????????} ????} //定義一個空的缺省方法,只返回?true boolean?customerLike()?{ returntrue; ????} } ``` 如上,我們加了一個邏輯判斷,邏輯判斷的方法時一個只返回 true 的方法,這個方法我們叫做鉤子方法。 > 鉤子:在模板方法的父類中,我們可以定義一個方法,它默認不做任何事,子類可以視情況要不要覆蓋它,該方法稱為“鉤子”。 鉤子方法一般是空的或者有默認實現。鉤子的存在,可以讓子類有能力對算法的不同點進行掛鉤。而要不要掛鉤,又由子類去決定。 是不是很有用呢,我們再看下咖啡的制作 ``` publicclass?Coffee?extends?Drinks?{ @Override void?brew()?{ ????????System.out.println("沖咖啡粉"); ????} @Override void?addCondiments()?{ ????????System.out.println("加奶加糖"); ????} //覆蓋了鉤子,提供了自己的詢問功能,讓用戶輸入是否需要加料 boolean?customerLike()?{ ????????String?answer?=?getUserInput(); if?(answer.toLowerCase().startsWith("y"))?{ returntrue; ????????}?else?{ returnfalse; ????????} ????} //處理用戶的輸入 private?String?getUserInput()?{ ????????String?answer?=?null; ????????System.out.println("您想要加奶加糖嗎?輸入 YES 或 NO"); ????????BufferedReader?reader?=?new?BufferedReader(new?InputStreamReader(System.in)); try?{ ????????????answer?=?reader.readLine(); ????????}?catch?(IOException?e)?{ ????????????e.printStackTrace(); ????????} if?(answer?==?null)?{ return"no"; ????????} return?answer; ????} } ``` 接著再去測試下代碼,看看結果吧。 ![](https://img.kancloud.cn/48/60/48605d7c8b5cfd3a7a428da6dfc00bf6_1080x202.png) 我想你應該知道鉤子的好處了吧,它可以作為條件控制,影響抽象類中的算法流程,當然也可以什么都不做。 模板方法有很多種實現,有時看起來可能不是我們所謂的“中規中矩”的設計。接下來我們看下 JDK 和 Spring 中是怎么使用模板方法的。 ## 四、 JDK 中的模板方法 我們寫代碼經常會用到comparable比較器來對數組對象進行排序,我們都會實現它的`compareTo()`方法,之后就可以通過`Collections.sort()`或者`Arrays.sort()`方法進行排序了。 具體的實現類就不寫了(可以去 github:starfish-learning 上看我的代碼),看下使用 ``` @Override public?int?compareTo(Object?o)?{ ????Coffee?coffee?=?(Coffee)?o; if(this.price?<?(coffee.price)){ return?-1; ????}elseif(this.price?==?coffee.price){ return0; ????}else{ return1; ????} } ``` ``` public?static?void?main(String\[\]?args)?{ ??Coffee\[\]?coffees?=?{new?Coffee("星冰樂",38), new?Coffee("拿鐵",32), new?Coffee("摩卡",35)}; ??Arrays.sort(coffees); for?(Coffee?coffee1?:?coffees)?{ ????System.out.println(coffee1); ??} } ``` ![](https://img.kancloud.cn/99/68/9968a38ea9491ecbe897e525e4260e7e_1080x151.png) 你可能會說,這個看著不像我們常規的模板方法,是的。我們看下比較器實現的步驟 1. 構建對象數組 2. 通過 Arrays.sort 方法對數組排序,傳參為`Comparable`接口的實例 3. 比較時候會調用我們的實現類的`compareTo()`方法 4. 將排好序的數組設置進原數組中,排序完成 一臉懵逼,這個實現竟然也是模板方法。 這個模式的重點在于提供了一個固定算法框架,并讓子類實現某些步驟,雖然使用繼承是標準的實現方式,但通過回調來實現,也不能說這就不是模板方法。 其實并發編程中最常見,也是面試必問的 AQS 就是一個典型的模板方法。 ## 五、Spring 中的模板方法 Spring 中的設計模式太多了,而且大部分擴展功能都可以看到模板方式模式的影子。 我們看下 IOC 容器初始化時中的模板方法,不管是 XML 還是注解的方式,對于核心容器啟動流程都是一致的。 `AbstractApplicationContext`的`refresh`方法實現了 IOC 容器啟動的主要邏輯。 一個`refresh()`方法包含了好多其他步驟方法,像不像我們說的模板方法,`getBeanFactory()`、`refreshBeanFactory()`是子類必須實現的抽象方法,`postProcessBeanFactory()`是鉤子方法。 ``` publicabstractclass?AbstractApplicationContext?extends?DefaultResourceLoader ??????implements?ConfigurableApplicationContext?{ @Override public?void?refresh()?throws?BeansException,?IllegalStateException?{ synchronized?(this.startupShutdownMonitor)?{ ???prepareRefresh(); ???ConfigurableListableBeanFactory?beanFactory?=?obtainFreshBeanFactory(); ???prepareBeanFactory(beanFactory); ????????????postProcessBeanFactory(beanFactory); ????????????invokeBeanFactoryPostProcessors(beanFactory); ????????????registerBeanPostProcessors(beanFactory); ????????????initMessageSource(); ????????????initApplicationEventMulticaster(); ????????????onRefresh(); ????????????registerListeners(); ????????????finishBeanFactoryInitialization(beanFactory); ????????????finishRefresh(); ??} ?} //?兩個抽象方法 @Override public?abstract?ConfigurableListableBeanFactory?getBeanFactory()?throws???IllegalStateException;? protected?abstract?void?refreshBeanFactory()?throws?BeansException,?IllegalStateException; //鉤子方法 protected?void?postProcessBeanFactory(ConfigurableListableBeanFactory?beanFactory)?{ ?} ?} ``` 打開你的 IDEA,我們會發現常用的`ClassPathXmlApplicationContext`和`AnnotationConfigApplicationContext`啟動入口,都是它的實現類(子類的子類的子類的...)。 `AbstractApplicationContext`的一個子類`AbstractRefreshableWebApplicationContext`中有鉤子方法`onRefresh()`的實現: ``` publicabstractclass?AbstractRefreshableWebApplicationContext?extends?……?{ /\*\* ??\*?Initialize?the?theme?capability. ??\*/ @Override protected?void?onRefresh()?{ this.themeSource?=?UiApplicationContextUtils.initThemeSource(this); ?} } ``` ![](https://img.kancloud.cn/31/df/31dfbcc2a55fae14c1d68d3af5481621_1080x674.png) ## 六、小總結 優點:1、封裝不變的部分,擴展可變的部分。2、提取公共代碼,便于維護。3、行為由父類控制,子類實現。 缺點:每一個不同的實現都需要一個子類來實現,導致類的個數增加,使得系統更加龐大。 使用場景:1、有多個子類共有的方法,且邏輯相同。2、重要的、復雜的方法,可以考慮作為模板方法。 注意事項:為防止惡意操作,一般模板方法都加上 final 關鍵詞。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看