對于開發人員來說,設計模式有時候就是一道坎,但是設計模式又非常有用,過了這道坎,它可以讓你水平提高一個檔次。而在android開發中,必要的了解一些設計模式又是必須的,因為設計模式在Android源碼中,可以說是無處不在。對于想系統的學習設計模式的同學,這里推薦一本書,《大話設計模式》。
Android常用設計模式系列:
面向對象的基礎特征
面向對象的設計原則
單例模式
模板模式
適配器模式
工廠模式
代理模式
原型模式
策略模式
Build模式
觀察者模式
裝飾者模式
中介模式
門面模式
模板模式
模板模式是非常常見的設計模式之一,寫個筆記,記錄一下我的學習過程和心得。
首先了解一些模板方法模式的定義。
定義一個操作中的算法的框架,而將一些步驟延遲到子類中。使得子類可以不改變一個算法的結構即可重新定義該算法的某些特定步驟。
齋看定義,也是有點難理解的,但是結合我們的開發,就比較好理解了,因為每個寫過代碼的人都應該或多或少的用過。
結合我們平時的一些設計,一般會涉及到對某一行為或者做某一件事情的抽象及對其的實現:
抽象父類(AbstractClass):實現了模板方法,定義了算法的骨架。
具體子類(ConcreteClass):實現抽象類中的抽象方法,即不同的對象的具體實現細節。
來一個簡單的例子吧,程序猿小波覺得搞開發寫代碼太苦逼而且找不到女票,而且考慮到之前無意間得到一本武林秘籍《thinking in 糕點》,也嘗試做過幾個美味可口的蛋糕。于是,他決定離職自己創業開了個蛋糕店,然后再招個漂亮的女服務員(關鍵是要單身,然后內部消化),這樣就OK了。
小波經過日夜修煉秘籍,掌握各式糕點的做法,并且融匯貫通,還總結了一套做各式蛋糕的流程:
- 材料,各式蛋糕需要各種不同的配料;
- 器具,各式蛋糕需要各種不同的器具;
- 時間,各式蛋糕成型的時間不一。
public abstract class AbstractCake {
/**
* 具體的整個過程
*/
public void doCake(){
this.doWhat();
this.burden();
this.appliance();
this.time();
}
/**
* 選擇做什么蛋糕
*/
protected abstract void doWhat();
/**
* 備什么材料
*/
protected abstract void burden();
/**
* 選用什么器具
*/
protected abstract void appliance();
/**
* 做多長時間
*/
protected abstract void time();
}
店鋪馬上要開張了,并且鎂鋁服務員也招到了,小波也做過市場調研,馬上開始了做最受歡迎的幾款蛋糕如下:
草莓奶油蛋糕-StrawberryCake
/**
* 小波蛋糕店版權所有,2018-09-01 21:25:25
* @author 小波
*/
public class StrawberryCake extends AbstractCake {
@Override
protected void doWhat() {
System.out.println("草莓奶油蛋糕");
}
@Override
protected void burden() {
System.out.println("我去買草莓和奶油");
}
@Override
protected void appliance() {
System.out.println("只需要烘焙機");
}
@Override
protected void time() {
System.out.println("烘焙十五分鐘搞定");
}
}
栗蓉暗香-ChestnutCake
/**
* 小波蛋糕店版權所有,2018-09-01 21:25:25
* @author 小波
*/
public class ChestnutCake extends AbstractCake {
@Override
protected void doWhat() {
System.out.println("栗蓉暗香蛋糕");
}
@Override
protected void burden() {
System.out.println("我去買栗子");
}
@Override
protected void appliance() {
System.out.println("需要十字小道、電餅鐺和烘焙機");
}
@Override
protected void time() {
System.out.println("烘焙十六分鐘搞定");
}
}
榴蓮飄飄-DurianCake
/**
* 小波蛋糕店版權所有,2018-09-01 21:25:25
* @author 小波
*/
public class DurianCake extends AbstractCake {
@Override
protected void doWhat() {
System.out.println("榴蓮飄飄蛋糕");
}
@Override
protected void burden() {
System.out.println("我去買榴蓮");
}
@Override
protected void appliance() {
System.out.println("需要刀、烘焙機");
}
@Override
protected void time() {
System.out.println("烘焙十二分鐘搞定");
}
}
開始做蛋糕
/**
* 小波蛋糕店版權所有,2018-09-01 21:25:25
* @author 小波
*/
public class App {
public static void main(String[] args) {
AbstractCake strawberry = new StrawberryCake();
strawberry.doCake();
System.out.println("-----------------------------");
AbstractCake chestnut = new ChestnutCake();
chestnut.doCake();
System.out.println("-----------------------------");
AbstractCake durian = new DurianCake();
durian.doCake();
}
}
輸出如下:
草莓奶油蛋糕
我去買草莓和奶油
只需要烘焙機
烘焙十五分鐘搞定
-----------------------------
栗蓉暗香蛋糕
我去買草莓和奶油
需要十字小道、電餅鐺和烘焙機
烘焙十六分鐘搞定
-----------------------------
榴蓮飄飄蛋糕
我去買榴蓮
需要刀、烘焙機
烘焙十二分鐘搞定
這樣我們就實現了使用模板模式的一個完整的實例。
廣泛應用
在Android的源碼中,模板模式的使用可以說無處不在
1 系統啟動過程
2.組件生命周期,比如Activity和Service等
3.一些具體封裝類,比如AsyncTask等
Activity和Service是每個接觸過Android學習的人都會非常的了解,這里不做詳細的分析。我們主要分析一下常用的AsyncTask。
首先眾所周知,AsyncTask的模板就是那幾個抽象方法,等你去實現,我們每次使用這個類,就是實現一個具體的子類,而且一個對象只能用一次。
1.onPreExecute()
2.doInBackground()
3.onProgressUpdate()
4.onPostExecute()
抽象父類的細節
首先介紹幾個角色:
1.ThreadPool:AsyncTask默認用線程池來切換線程,這個線程池在不同Android版本是不一樣的,最初始串行的,后來是并行,現在又是串行,是一個全局的線程池。
2.Handler:這個用于線程的消息交互,主要是子線程通知到UI線程,因為onPostEx’ecute()是在UI線程,所以我們handler必須在U線程初始化。
為什么AsyncTask必須在UI線程初始化呢?
就是以為內部的Handler必須是綁定UI線程的,而這個handler綁定的線程是也是在AsyncTask初始化的當前線程。
介紹模板調用細節
1.首先執行AsyncTask.Execute(Params)
這個方法會調用ExecuteOnExecutor(Executor,Params),會傳入默認的線程池:sDefaultExecutor和參數
2.ExecuteOnExecutor(Executor,Params)
這個方法會做以下幾件事:1.開始判斷AsyncTask對象是否處于Running和Finish狀態,如果是就會彈出異常。然后設定這個AsyncTask對象處于running狀態。這也是為什么一個AsyncTask對象只能用一次。
2.調用onPreExecute()做一些初始化和準備。
3.調用doInBackground,初始化mWorker,并把Params賦值給mWorker的mParams成員。mWorker實現了Callable接口并在視線中調用了postResult(doInBackground(Params)),這個方法是關鍵,完成了線程切換,我們后面展開講。
4.初始化mFuture,這是一個FutureTask對象,可以理解為一個Thread對象。
5.sDefaultExecutor.execute(mFuture),mFuture的run()方法會調用mWorker的call回調方法,最終調用postResult(doInBackground(Params))
- postResult(doInBackground(Params))方法
以上是onPreExecute()和doInBackground()方法已經被調用了
postResult(doInBackground(Params))這個方法做了一下幾個事。1.構造一個AsyncTaskResult,這個對象是兩個對象構造:
AsyncTask對象實例
doInBackground執行完的Result
2.構造一個UI線程Handler的Message,what是MESSAGE_POST_RESULT,obj是AsyncTaskResult
3.發送給UI線程Handler處理。這個handler能處理兩類消息就是:
MESSAGE_POST_RESULT:收到這個消息后切換到UI線程,取出obj里面的AsyncTask對象實例調用finish()方法,finish方法會調用>onPostExecute()方法,并且把狀態標為finish。
MESSAGE_POST_PROGRESS消息:
會調用AsyncTask對象實例的onUpdateProgress()方法
以上就是AsyncTask的模板流程,我們只需要實現流程中一些對我們開放的特有的細節(就是前文提到的幾個接口)就OK,無法影響整個流程的結構。
總結
其實,模板模式說得通俗點一點就是 :完成一件事情,有固定的數個步驟,但是每個步驟根據對象的不同,而實現細節不同;就可以在父類中定義一個完成該事情的總方法,按照完成事件需要的步驟去調用其每個步驟的實現方法。每個步驟的具體實現,由子類完成。
接下來,總結一下模板模式的優缺點
優點
- 具體細節步驟實現定義在子類中,子類定義詳細處理算法是不會改變算法整體結構。
- 代碼復用的基本技術,在數據庫設計中尤為重要。
- 存在一種反向的控制結構,通過一個父類調用其子類的操作,通過子類對父類進行擴展增加新的行為,符合“開閉原則”。
缺點
每個不同的實現都需要定義一個子類,會導致類的個數增加,系統更加龐大。
適用場景
- 在多個子類中擁有相同的方法,而且邏輯相同時,可以將這些方法抽出來放到一個模板抽象類中。
- 程序主框架相同,細節不同的情況下,也可以使用模板方法。