設計模式之模板方法模式實戰解析

本文微信公衆號「AndroidTraveler」首發。java

背景

最近在看《設計模式之禪》,爲了可以更加深刻的理解設計模式,達到學以至用。
這邊記錄一下本身的一些感覺和見解,並結合具體代碼實戰來進行說明。算法

模板方法模式

但凡和設計模式掛上鉤,咱們老是會以爲「遙不可及」。
然而實際上,設計模式是基於大量實際代碼的經驗總結,它來自於實際的代碼。
與其說「遙不可及」,其實它反而是比較「接地氣」。
而模板方法模式相信你看完本篇文章以後,會發現,原來這就是模板方法模式,而後就去看你以前的代碼了。設計模式

小例子初識模板方法模式

理解設計模式最好的方法就是經過項目開發中的實際場景來講明。微信

你們作 Android 開發的時候寫 Activity 應該都會看到下面相似代碼吧?框架

private void getIntents() {
    // 從 Intent 獲取傳遞過來的一些參數,設置到屬性中
}

private void findViewById() {
    // 經過 findViewById 來初始化各個組件
}

private void setViews() {
    // 給組件設置監聽或者初始狀態
}

複製代碼

假設我每一個界面都這樣寫,那麼重複代碼太多了,很不必。
雖然每一個方法具體的邏輯不同,可是都有這些操做。ide

這個時候咱們第一個想法就是繼承,抽取出一個 BaseActivity。
而後將這些通用代碼都放到了 BaseActivity 裏面,子類再來覆寫就能夠了。spa

可是還有一個問題,那就是,我每次都須要寫下面代碼:設計

getIntents();
findViewById();
setViews();
複製代碼

尤爲是通用代碼多的時候,有時候手誤可能致使某些界面這三個方法調用順序還不同。
那怎麼辦呢?咱們能夠抽取出一個方法,這個方法表明了這三個方法統一的調用順序,這樣就不怕手誤寫錯了。
而這個方法就是咱們的模板方法3d

public abstract class BaseActivity extends Activity {
    /** * 從 Intent 獲取傳遞過來的一些參數,設置到屬性中 */
    protected abstract void getIntents();

    /** * 經過 findViewById 來初始化各個組件 */
    protected abstract void findViewById();

    /** * 給組件設置監聽或者初始狀態 */
    protected abstract void setViews();

    /** * 模板方法 */
    final public void init() {
        getIntents();
        findViewById();
        setViews();
    }
}
複製代碼

這樣我後面的 Activity 均可以繼承這個 BaseActivity,而後只須要調用 init 方法便可。
至於不一樣的 Activity 的邏輯我再在三個方法裏面各自實現便可。code

鉤子方法

一聽到這個詞,是否是以爲有點「高大上」,彷佛很 NB?
然而,聽完我後面的講述,你心裏估計

咱們上面的方法裏面,並非全部的 Activity 都有其餘 Activity 傳遞數據過來的,所以 getIntents 這個方法不必定全部子類都要調用。
這個時候咱們能夠提供一個鉤子方法,改動部分代碼以下:

/** * 模板方法 */
final public void init() {
    if (isGetIntents()) {
        getIntents();
    }
    findViewById();
    setViews();
}

/** * 鉤子方法,是否須要設置數據,默認爲 true * @return */
protected boolean isGetIntents() {
    return true;
}
複製代碼

能夠看到,經過鉤子方法,當咱們默認須要獲取數據時,什麼都不用改動,若是咱們不須要獲取數據,只須要覆寫咱們的鉤子方法 isGetIntents 並返回 false 便可。

好了,有了初步的印象以後,接下來就正式的加深瞭解吧。

定義

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.
定義一個操做中的算法的框架,而將一些步驟延遲到子類中。使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。

簡單的說就是父類定義了一個模板方法,在這個模板方法裏面有一些特定的步驟。具體的步驟實現留給子類去處理。

父類的模板方法保持了各個子類的共性,模板方法裏面的步驟使得每一個子類都有本身的個性。

通用代碼實現

父類:

public abstract class AbstractPatternClass {
    /** * 基本方法,模板方法裏面調用 */
    protected abstract void firstModule();
    /** * 基本方法,模板方法裏面調用 */
    protected abstract void secondModule();

    /** * 模板方法,多個基本方法組合 */
    final public void templateMethod() {
        firstModule();
        secondModule();
    }
}
複製代碼

具體子類:

public class ConcreteClass extends AbstractPatternClass {
    @Override
    protected void firstModule() {
        // TODO 子類實現本身的邏輯
    }

    @Override
    protected void secondModule() {
        // TODO 子類實現本身的邏輯
    }
}
複製代碼

場景使用類:

public class PatternTest {
    public static void main(String[] args) {
        AbstractPatternClass abstractPatternClass = new ConcreteClass();
        // 調用模板方法
        abstractPatternClass.templateMethod();
    }
}
複製代碼

鉤子方法咱們上面已經說過了,相信聰明的你知道如何使用,這裏就再也不贅述了。

注意點

父類中的基本方法儘可能設計爲 protected 類型,符合迪米特法則。
父類中的模板方法通常設置爲 final,不容許子類覆寫。這樣的目的一個是爲了不子類惡意操做,一個是爲了模板的共性。

應用

當你在寫代碼常常用到複製和粘貼快捷鍵時,你就要考慮是否是能夠進行抽取。
當你修改一個地方的時候,發現其餘地方也要連帶修改,也須要考慮一下。
多個子類有公共方法,而且邏輯基本相同。
複雜的一些算法之類的,可讓子類經過基本方法傳遞一些參數,核心邏輯放在模板方法裏面。
重構項目的時候,也能夠考慮一下把相同代碼抽取到父類,經過鉤子方法定製化模板。

結語

最後一點就是注意不要濫用設計模式,不要爲了設計而設計

參考資料
設計模式之禪(第2版)

相關文章
相關標籤/搜索