重識設計模式-模版方法模式(Template Method Pattern)

本文已同步發表到個人技術微信公衆號,掃一掃文章底部的二維碼或在微信搜索 「程序員驛站」便可關注,不按期更新優質技術文章。同時,也歡迎加入QQ技術羣(羣號:650306310)一塊兒交流學習!java

定義

模版方法模式(Template Method Pattern): 定義一個操做中算法的框架,而將一些步驟延遲到子類中,使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。模板方法模式是一種基於繼承的代碼複用技術,它是一種類行爲型模式。程序員

角色

模板方法模式是結構最簡單的行爲型設計模式,在其結構中只存在父類與子類之間的繼承關係。經過使用模板方法模式,能夠將一些複雜流程的實現步驟封裝在一系列基本方法中,在抽象父類中提供一個稱之爲模板方法的方法來定義這些基本方法的執行次序,而經過其子類來覆蓋某些步驟,從而使得相同的算法框架能夠有不一樣的執行結果。模板方法模式提供了一個模板方法來定義算法框架,而某些具體步驟的實現能夠在其子類中完成。算法

模板方法模式結構比較簡單,其核心是抽象類和其中的模板方法的設計,其結構如圖所示:設計模式

模板方法模式結構圖

模板方法模式包含以下兩個角色:bash

●AbstractClass( 抽象類): 在抽象類中定義了一系列基本操做(PrimitiveOperations),這些基本操做能夠是具體的,也能夠是抽象的,每個基本操做對應算法的一個步驟,在其子類中能夠重定義或實現這些步驟。同時,在抽象類中實現了一個模板方法(Template Method),用於定義一個算法的框架,模板方法不只能夠調用在抽象類中實現的基本方法,也能夠調用在抽象類的子類中實現的基本方法,還能夠調用其餘對象中的方法。微信

●ConcreteClass( 具體子類): 它是抽象類的子類,用於實如今父類中聲明的抽象基本操做以完成子類特定算法的步驟,也能夠覆蓋在父類中已經實現的具體基本操做。框架

模板實現

在實現模板方法模式時,開發抽象類的軟件設計師和開發具體子類的軟件設計師之間能夠進行協做。一個設計師負責給出一個算法的輪廓和框架,另外一些設計師則負責給出這個算法的各個邏輯步驟。實現這些具體邏輯步驟的方法即爲基本方法,而將這些基本方法彙總起來的方法即爲模板方法,模板方法模式的名字也所以而來。下面將詳細介紹模板方法和基本方法:ide

●模板方法: 一個模板方法是定義在抽象類中的、把基本操做方法組合在一塊兒造成一個總算法或一個總行爲的方法。這個模板方法定義在抽象類中,並由子類不加以修改地徹底繼承下來。模板方法是一個具體方法,它給出了一個頂層邏輯框架,而邏輯的組成步驟在抽象類中能夠是具體方法,也能夠是抽象方法。因爲模板方法是具體方法,所以模板方法模式中的抽象層只能是抽象類,而不是接口。函數

●基本方法: 基本方法是實現算法各個步驟的方法,是模板方法的組成部分。基本方法又能夠分爲三種:抽象方法(Abstract Method)、具體方法(Concrete Method)和鉤子方法(Hook Method)。源碼分析

(1).抽象方法(Abstract Method): 一個抽象方法由抽象類聲明、由其具體子類實現。在C#語言裏一個抽象方法以abstract關鍵字標識。

(2).具體方法(Concrete Method): 一個具體方法由一個抽象類或具體類聲明並實現,其子類能夠進行覆蓋也能夠直接繼承。

(3).鉤子方法(Hook Method): 一個鉤子方法由一個抽象類或具體類聲明並實現,而其子類可能會加以擴展。一般在父類中給出的實現是一個空實現(可以使用virtual關鍵字將其定義爲虛函數),並以該空實現做爲方法的默認實現,固然鉤子方法也能夠提供一個非空的默認實現。

模板方法模式中,鉤子方法有兩類:第一類鉤子方法能夠與一些具體步驟「掛鉤」,以實如今不一樣條件下執行模板方法中的不一樣步驟,這類鉤子方法的返回類型一般是bool類型的,這類方法名通常爲IsXXX(),用於對某個條件進行判斷,若是條件知足則執行某一步驟,不然將不執行,在模板方法模式中,抽象類的典型代碼以下:

AbstractClass.java

/** * Created by Coding小僧 on 2019/3/19 * * @since 1.0 */
public abstract class AbstractClass {
    //模板方法
    public final void templateMethod() {
        open();
        display();
        //經過鉤子方法來肯定某步驟是否執行
        if (isPrint()) {
            print();
        }
    }

    protected void open() {
        System.out.println("open");
    }

    protected void display() {
        System.out.println("display");
    }

    //鉤子方法
    protected boolean isPrint() {
        return true;
    }

    protected void print() {
        System.out.println("print");
    }
}
複製代碼

在代碼中IsPrint()方法便是鉤子方法,它能夠決定Print()方法是否執行,通常狀況下,鉤子方法的返回值爲true,若是不但願某方法執行,能夠在其子類中覆蓋鉤子方法,將其返回值改成alse便可,這種類型的鉤子方法能夠控制方法的執行,對一個算法進行約束。

還有一類鉤子方法就是實現體爲空的具體方法,子類能夠根據須要覆蓋或者繼承這些鉤子方法,與抽象方法相比,這類鉤子方法的好處在於子類若是沒有覆蓋父類中定義的鉤子方法,編譯能夠正常經過,可是若是沒有覆蓋父類中聲明的抽象方法,編譯將報錯。在模板方法模式中,抽象類的典型代碼以下:

AbstractClass.java

/** * Created by Coding小僧 on 2019/3/19 * * @since 1.0 */
public abstract class AbstractClass {

    //模板方法
    public final void templateMethod() {
        primitiveOperation1();
        primitiveOperation2();
        primitiveOperation3();
    }

    //基本方法—具體方法
    public void primitiveOperation1() {
        //實現代碼
    }

    //基本方法—抽象方法
    public abstract void primitiveOperation2();

    //基本方法—鉤子方法
    public void primitiveOperation3() {
    }
}
複製代碼

在抽象類中,模板方法templateMethod()定義了算法的框架,在模板方法中調用基本方法以實現完整的算法,每個基本方法如primitiveOperation1()、PrimitiveOperation2()等均實現了算法的一部分,對於全部子類都相同的基本方法可在父類提供具體實現,例如primitiveOperation1(),不然在父類聲明爲抽象方法或鉤子方法,由不一樣的子類提供不一樣的實現,例如primitiveOperation2()和PrimitiveOperation3()。

可在抽象類的子類中提供抽象步驟的實現,也可覆蓋父類中已經實現的具體方法,具體子類的典型代碼以下:

ConcreteClass.java

/** * Created by Coding小僧 on 2019/3/19 * * @since 1.0 */
public class ConcreteClass extends AbstractClass {

    @Override
    public void primitiveOperation2() {
        //實現代碼
    }

    @Override
    public void primitiveOperation3() {
        //實現代碼
    }
}
複製代碼

模板方法模式中,因爲面向對象的多態性,子類對象在運行時將覆蓋父類對象,子類中定義的方法也將覆蓋父類中定義的方法,所以程序在運行時,具體子類的基本方法將覆蓋父類中定義的基本方法,子類的鉤子方法也將覆蓋父類的鉤子方法,從而能夠經過在子類中實現的鉤子方法對父類方法的執行進行約束,實現子類對父類行爲的反向控制。

案例回放

咱們將建立一個定義操做的 Game 抽象類,其中,模板方法設置爲final,這樣它就不會被重寫。Cricket 和 Football 是擴展了 Game 的實體類,它們重寫了抽象類的方法。

本實例結構圖如圖:

在上圖中,Game充當抽象類角色,Cricket和Football充當具體子類角色。完整代碼以下所示:

Game.java

public abstract class Game {
   abstract void initialize();
   abstract void startPlay();
   abstract void endPlay();
 
   //模板
   public final void play(){
      //初始化遊戲
      initialize();
 
      //開始遊戲
      startPlay();
 
      //結束遊戲
      endPlay();
   }
}
複製代碼

Cricket.java

public class Cricket extends Game {
 
   @Override
   void endPlay() {
      System.out.println("Cricket Game Finished!");
   }
 
   @Override
   void initialize() {
      System.out.println("Cricket Game Initialized! Start playing.");
   }
 
   @Override
   void startPlay() {
      System.out.println("Cricket Game Started. Enjoy the game!");
   }
}
複製代碼

Football.java

public class Football extends Game {
 
   @Override
   void endPlay() {
      System.out.println("Football Game Finished!");
   }
 
   @Override
   void initialize() {
      System.out.println("Football Game Initialized! Start playing.");
   }
 
   @Override
   void startPlay() {
      System.out.println("Football Game Started. Enjoy the game!");
   }
}
複製代碼

TemplatePatternDemo.java

public static void main(String[] args) {
      Game game = new Cricket();
      game.play();
      System.out.println();
      game = new Football();
      game.play();      
   }
}
複製代碼

編譯並運行程序,輸出結果以下:

Cricket Game Initialized! Start playing.
Cricket Game Started. Enjoy the game!
Cricket Game Finished!

Football Game Initialized! Start playing.
Football Game Started. Enjoy the game!
Football Game Finished!
複製代碼

典型應用

在Android中,使用了模板方法且爲咱們熟知的一個典型類就是AsyncTask了,關於AsyncTask的更詳細的分析請移步:

Android中AsyncTask的使用與源碼分析:blog.csdn.net/bboyfeiyu/a…

咱們這裏只分析在該類中使用的模板方法模式。在使用AsyncTask時,咱們都有知道耗時的方法要放在doInBackground(Params... params)中,在doInBackground以前若是還想作一些相似初始化的操做能夠寫在onPreExecute方法中,當doInBackground方法執行完成後,會執行onPostExecute方法,而咱們只須要構建AsyncTask對象,而後執行execute方法便可。咱們能夠看到,它整個執行過程實際上是一個框架,具體的實現都須要子類來完成。並且它執行的算法框架是固定的,調用execute後會依次執行onPreExecute,doInBackground,onPostExecute,固然你也能夠經過onProgressUpdate來更新進度。咱們能夠簡單的理解爲以下圖的模式:

優勢

模板方法模式的主要優勢以下:

(1) 在父類中形式化地定義一個算法,而由它的子類來實現細節的處理,在子類實現詳細的處理算法時並不會改變算法中步驟的執行次序。

(2) 模板方法模式是一種代碼複用技術,它在類庫設計中尤其重要,它提取了類庫中的公共行爲,將公共行爲放在父類中,而經過其子類來實現不一樣的行爲,它鼓勵咱們恰當使用繼承來實現代碼複用。

(3) 可實現一種反向控制結構,經過子類覆蓋父類的鉤子方法來決定某一特定步驟是否須要執行。

(4) 在模板方法模式中能夠經過子類來覆蓋父類的基本方法,不一樣的子類能夠提供基本方法的不一樣實現,更換和增長新的子類很方便,符合單一職責原則和開閉原則

缺點

模板方法模式的主要缺點以下:

須要爲每個基本方法的不一樣實現提供一個子類,若是父類中可變的基本方法太多,將會致使類的個數增長,系統更加龐大,設計也更加抽象,此時,可結合橋接模式來進行設計。

重點

模板方法模式是基於繼承的代碼複用技術,它體現了面向對象的諸多重要思想,是一種使用較爲頻繁的模式。模板方法模式普遍應用於框架設計中,以確保經過父類來控制處理流程的邏輯順序(如框架的初始化,測試流程的設置等)。爲防止惡意操做,通常模板方法都加上 final 關鍵詞。

使用場景

在如下狀況下能夠考慮使用模板方法模式:

(1) 對一些複雜的算法進行分割,將其算法中固定不變的部分設計爲模板方法和父類具體方法,而一些能夠改變的細節由其子類來實現。即:一次性實現一個算法的不變部分,並將可變的行爲留給子類來實現。

(2) 各子類中公共的行爲應被提取出來並集中到一個公共父類中以免代碼重複。

(3) 須要經過子類來決定父類算法中某個步驟是否執行,實現子類對父類的反向控制

關注個人技術公衆號"程序員驛站",不更新技術文章,微信掃一掃下方二維碼便可關注:

相關文章
相關標籤/搜索