Java設計模式系列之——模板方法模式

大事件

 

關注籃球或者喜歡逛社交網站的朋友們可能都知道,2019年10月5日,NBA休斯頓火箭隊總經理莫雷在推特上發佈了一張無知的涉港圖片,引發全體中國人民的強烈抗議和不滿,一時間輿論譁然,此後,NBA總裁肖華迴應此時稱支持莫雷,再度引爆輿論。事件發展到今天已經過了一週了,主要矛盾製造者、當事人莫雷和笑話無任何道歉悔意,甚至將矛盾轉移到兩國之間的各種人羣中。其態度傲慢、用心險惡,公然干涉他國內政和主權完整,這次我們一定不會善罷甘休。

 

這些天,推特、微博、虎撲、知乎等各大網站都因爲此事件戰火瀰漫,網民都在有關此事件的新聞下各顯神通,使出渾身解數,各種評論段子層出不窮,各種污言穢語不堪入目。在這兒也請同胞們不要被不懷好意的人利用輿論謾罵自己同胞,不要自己內部開始瓦解,我們只要抓住主要矛盾,莫雷必須站出來道歉,必須辭去火箭隊總經理職位,肖華也必須站出來道歉,解僱莫雷。這纔是道歉最基本的誠意,纔有可能挽回一點中國球迷的心。

 

當然時間最終會給我們一個答案,在等待答案的日子裏,全球各地,社會各界會有越來越多的人就此事發聲,我們當然也不會閒着,在各大社交媒體軟件上每天“問候”莫雷,以表示我們的“言論自由”。

 

 

這是湖人球員詹姆斯今天接受媒體採訪後,連發兩條推特談論莫雷事件,目前下面評論都是噴他的,可以說裏外不是人了。不過這不是本篇的重點,作爲技術公衆號,我們從技術的角度來思考一下,這件事發生以來,輿論是怎樣發酵的?是通過什麼樣的方式呢?來進入我們今天的主題,Java設計模式之——模板方法模式。

 

 

什麼是模板方法模式

 

Define the skeleton of an algorithm in an operation,deferring some steps to subclasses.

Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's  structure.

 

模板方法模式(Template Method Pattern):定義一個操作中的算法的框架,而將一些步驟延遲到子類中。模板方法允許子類在不改變算法結構的情況下重新定義算法的某些步驟。模板方法模式屬於行爲型模式,主要使用Java的繼承機制來實現。

 

模板方法,顧名思義就是一個模板,按照模板做事,模板方法裏封裝了方法的執行步驟,調用方只需要通過調用模板就能按照正確的順序把事情做好。

 

模板方法模式的兩個角色

 

  • AbstractClass(抽象類):定義了一系列基本操作(PrimitiveOperations),這些基本操作(方法)可以是具體的,也可以是抽象的,每一個基本操作對應算法的一個步驟,在其子類中可以重定義或實現這些步驟。同時,在抽象類中定義了一個模板方法(Template Method),也就是定義了一個算法的框架,並定義組合好了這些操作的執行順序。

     

  • ConcreteClass(具體子類):抽象類的子類,用於實現在父類中聲明的抽象基本操作以完成子類特定算法的步驟,也可以覆蓋在父類中已經實現的具體基本操作。


 

模板方法模式UML圖

 

 

模板方法模式代碼演示

有小夥伴要問了,模板方法模式和莫雷事件有什麼關係嗎?他們二者之間本身並沒有任何關聯,我是在觀察大家在面對這件事的態度,以及我們表達我們觀點所採用的方式。再抽象一下,我們每個人對這件事的行爲是不是可以簡單的抽象成,打開社交軟件,發表觀點和意見、關閉社交軟件。這是什麼?運用到項目上來看,這不就是一個模板方法嗎?大家都是按照這個模板在表達觀點,不同的是有的人用的微博,有的人用的虎撲,有的用的推特,有的支持莫雷,有的罵莫雷等等。我們來看一下代碼實現。

 

1、編寫抽象模板類

package com.mazhichu.designpatterns.templatemethod;/** * <p class="detail"> * 功能: 抽象模板類 * </p> * * @author Moore * @ClassName Comment. * @Version V1.0. * @date 2019.10.16 11:53:02 */public abstract class Comment {    /**     * <p class="detail">     * 功能: 基本抽象方法     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    protected abstract void openSocialSoftware();    /**     * <p class="detail">     * 功能: 基本抽象方法     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    protected abstract void comment();    /**     * <p class="detail">     * 功能: 具體方法     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    protected void closeSocialSoftware(){        System.out.println("關閉社交軟件");    }    /**     * <p class="detail">     * 功能: 模板方法,模板方法,爲了防止惡意的操作,一般模板方法都加上final關鍵字,子類不可實現     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    public final void temtemplateMethod(){        openSocialSoftware();        comment();        closeSocialSoftware();    }}

 

 

2、編寫具體類

package com.mazhichu.designpatterns.templatemethod;/** * <p class="detail"> * 功能: 具體類 * </p> * * @author Moore * @ClassName Zhang san. * @Version V1.0. * @date 2019.10.16 14:00:09 */public class ZhangSan extends Comment {    /**     * <p class="detail">     * 功能: 基本抽象方法     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    @Override    protected void openSocialSoftware() {        System.out.println("打開微博!");    }    /**     * <p class="detail">     * 功能: 基本抽象方法     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    @Override    protected void comment() {        System.out.println("評論是:莫雷必須道歉,肖華必須道歉!解僱莫雷!");    }}
package com.mazhichu.designpatterns.templatemethod;/** * <p class="detail"> * 功能: 具體類 * </p> * * @author Moore * @ClassName Li si. * @Version V1.0. * @date 2019.10.16 13:59:56 */public class LiSi extends Comment {    /**     * <p class="detail">     * 功能: 基本抽象方法     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    @Override    protected void openSocialSoftware() {        System.out.println("打開虎撲");    }    /**     * <p class="detail">     * 功能: 基本抽象方法     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    @Override    protected void comment() {        System.out.println("評論是:莫雷是傻X,WQNMLGB");    }}
package com.mazhichu.designpatterns.templatemethod;/** * <p class="detail"> * 功能: 具體類 * </p> * * @author Moore * @ClassName James. * @Version V1.0. * @date 2019.10.16 13:59:01 */public class James extends Comment {    /**     * <p class="detail">     * 功能: 基本抽象方法     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    @Override    protected void openSocialSoftware() {        System.out.println("打開推特");    }    /**     * <p class="detail">     * 功能: 基本抽象方法     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    @Override    protected void comment() {        System.out.println("評論是:肖華應該懲罰莫雷!");    }}

 

3、客戶端調用

package com.mazhichu.designpatterns.templatemethod;public class Client {    public static void main(String[] args) {        Comment zhangSan = new ZhangSan();        zhangSan.temtemplateMethod();        System.out.println("-----------------\n");        Comment lisi = new LiSi();        lisi.temtemplateMethod();        System.out.println("-----------------\n");        Comment james = new James();        james.temtemplateMethod();    }}

 

查看運行結果:

 

 

上面就是模板方法模式,可能是最常見也是最簡單的設計模式了,父類(抽象類)定義算法和模板方法,模板方法約定好算法執行順序,子類只需要實現父類的抽象算法,就能按照模板方法既定的順序執行得到不同的結果,這樣做大大簡化了子類的複雜度。

 

 

鉤子方法

 

 

爲什麼把這個單獨拿出來說,是因爲在模板方法中有些方法是否執行是可選的,也就是不是所有模板中的算法都需要被執行,可以由子類來決定,這就是我們要說的“鉤子方法”。

鉤子方法是一種被聲明在抽象類中的方法,一般是空實現或者有默認值,子類可以實現覆蓋該鉤子,來決定算法步驟的某一步驟是否要執行,這是一種反向控制。

我們來改造一下抽象類,加入鉤子方法。

 

1、重新抽象類,加入鉤子方法

package com.mazhichu.designpatterns.templatemethod;/** * <p class="detail"> * 功能: 抽象模板類 * </p> * * @author Moore * @ClassName Comment. * @Version V1.0. * @date 2019.10.16 11:53:02 */public abstract class Comment {    /**     * <p class="detail">     * 功能: 基本抽象方法     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    protected abstract void openSocialSoftware();    /**     * <p class="detail">     * 功能: 基本抽象方法     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    protected abstract void comment();    /**     * <p class="detail">     * 功能: 具體方法     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    protected void closeSocialSoftware(){        System.out.println("關閉社交軟件");    }    /**     * <p class="detail">     * 功能: 模板方法,爲了防止惡意的操作,一般模板方法都加上final關鍵字,子類不可實現     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    public final void temtemplateMethod(){        openSocialSoftware();        if(isLogin()){            comment();        }else {            System.out.println("您未登錄,不可以發表評論哦,只能看別人罵莫雷");        }        closeSocialSoftware();    }    /**     * 鉤子方法:可以是抽象方法,空實現或默認實現,子類可以覆寫來決定模板方法是否執行某些算法     * @return     */    protected boolean isLogin(){        System.out.println("登錄後纔可以評論---");        return true;    }}

 

2、編寫一個覆蓋鉤子方法的子類

package com.mazhichu.designpatterns.templatemethod;public class LaoBao extends Comment {    /**     * <p class="detail">     * 功能: 基本抽象方法     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    @Override    protected void openSocialSoftware() {        System.out.println("打開知乎");    }    /**     * <p class="detail">     * 功能: 基本抽象方法     * </p>     *     * @author Moore     * @date 2019.10.16 11:53:02     */    @Override    protected void comment() {        System.out.println("我不但想罵莫雷肖華,還想問候川普");    }    @Override    protected boolean isLogin() {        return false;    }}

 

3、客戶端調用

package com.mazhichu.designpatterns.templatemethod;public class Client {    public static void main(String[] args) {        Comment zhangSan = new ZhangSan();        zhangSan.temtemplateMethod();        System.out.println("-----------------\n");        Comment lisi = new LiSi();        lisi.temtemplateMethod();        System.out.println("-----------------\n");        Comment james = new James();        james.temtemplateMethod();        System.out.println("-----------------\n");        Comment laobao = new LaoBao();        laobao.temtemplateMethod();    }}

 

4、查看運行結果:

通過結果可以得出,加入鉤子方法後,可以反向控制父類中的模板方法中某些算法是否要被執行,這樣也增加了模板方法的靈活性。

 

 

總結

 

模板方法模式優點

  • 良好的封裝性和擴展性。把公共行爲封裝在父類,通過子類實現具體行爲邏輯,增加功能由子類實現基本方法擴展,複合單一職責原則和開閉原則。

  • 提高代碼的複用性,模板方法模式就是一種代碼複用技術,在類庫設計中尤爲重要。

  • 反向控制,通過子類覆蓋父類的鉤子方法來決定某一特定步驟是否需要執行。

 

 

 

模板方法模式缺點

  • 模板方法使用繼承方式複用代碼,如果要在抽象類裏增加一個抽象方法,則每一個子類都要修改代碼。

  • 每一個不同的實現都需要通過一個子類來實現,導致類的個數增加,系統變得龐大。

 

 

 

模板方法應用場景

  • 多個子類中有公共的方法,並且邏輯相同,可以集中到公共父類中防止代碼重複。

  • 一些重要複雜的算法,可以把算法中不變的部分設計成模板方法,可變的部分由子類實現。

  • 需要通過子類來控制父類中某個算法是否執行,通過鉤子方法實現反向控制。

 

 

知識點,用筆記下來

 

  • 爲防止惡意操作,一般都在模板方法上加上 final 關鍵詞,防止子類改變模板方法中的算法執行順序。

  • 策略模式和模板方法都是用於封裝算法,前者是利用組合和委託模型,而後者則是繼承。

 

 

番外

 

 

上面就是關於模板方法模式的講解,其實很簡單,就是我們常用的封裝和繼承。這兒想說的是,其實設計模式沒有那麼枯燥與複雜,平常多留意我們生活中的日常瑣事,多思考,知識都來源於生活的觀察與總結,設計模式也在我們生活中的點點滴滴裏。最後,香港是中國的,任何國家、任何組織、任何勢力、任何個人都無權干預我國內政,無權干涉我們領土主權的完整。希望香港早日安寧下來,再談其他事。

 

 

我不能保證我寫的文章都是正確的,但是我能保證都是我自己花時間用心寫的,所有的代碼示例都是原創,所有的理解都只是我個人理解,不能代表官方權威,所以請各位讀者閱讀時帶着批判的眼光,有選擇性的認同,謝謝!

 

文章同步公衆號:碼之初,每天推送Java技術文章,期待您的關注!還可以加我的個人微信qingtian-1989,備註技術羣,一起在羣裏討論技術和生活。

原創不易,轉載請註明出處,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章