前言
Android的設(shè)計(jì)模式系列文章介紹座柱,歡迎關(guān)注,持續(xù)更新中:
Android的設(shè)計(jì)模式-設(shè)計(jì)模式的六大原則
一句話總結(jié)23種設(shè)計(jì)模式則
創(chuàng)建型模式:
Android的設(shè)計(jì)模式-單例模式
Android的設(shè)計(jì)模式-建造者模式
Android的設(shè)計(jì)模式-工廠方法模式
Android的設(shè)計(jì)模式-簡單工廠模式
Android的設(shè)計(jì)模式-抽象工廠模式
Android的設(shè)計(jì)模式-原型模式
行為型模式:
Android的設(shè)計(jì)模式-策略模式
Android的設(shè)計(jì)模式-狀態(tài)模式
Android的設(shè)計(jì)模式-責(zé)任鏈模式
Android的設(shè)計(jì)模式-觀察者模式
Android的設(shè)計(jì)模式-模板方法模式
Android的設(shè)計(jì)模式-迭代器模式
Android的設(shè)計(jì)模式-備忘錄模式
Android的設(shè)計(jì)模式-訪問者模式
Android的設(shè)計(jì)模式-中介者模式
Android的設(shè)計(jì)模式-解釋器模式
Android的設(shè)計(jì)模式-命令模式
結(jié)構(gòu)型模式:
Android的設(shè)計(jì)模式-代理模式
Android的設(shè)計(jì)模式-組合模式
Android的設(shè)計(jì)模式-適配器模式
Android的設(shè)計(jì)模式-裝飾者模式
Android的設(shè)計(jì)模式-享元模式
Android的設(shè)計(jì)模式-外觀模式
Android的設(shè)計(jì)模式-橋接模式
1.定義
定義一個(gè)操作中的算法框架迹鹅,而將一些步驟延遲到子類中,使得子類可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義算法的某些特定步驟贞言。
2.介紹
- 模板方法模式屬于行為型模式斜棚。
- 模板方法模式主要是用來定義一套流程下來的固定步驟,而具體的步驟實(shí)現(xiàn)則可以是不固定的。
3.UML類圖
角色說明:
AbstractClass(抽象類):弟蚀,定義了一整套算法框架蚤霞。
ConcreteClass(具體實(shí)現(xiàn)類):具體實(shí)現(xiàn)類,根據(jù)需要去實(shí)現(xiàn)抽象類中的方法粗梭。
4.實(shí)現(xiàn)
繼續(xù)以送快遞為例争便,快遞員送快遞基本就是一套固定的流程:收到快遞,準(zhǔn)備派送->聯(lián)系收貨人->確定結(jié)果断医。
4.1 創(chuàng)建抽象類
定義算法框架滞乙,這里是快遞員派送快遞的步驟:
public abstract class Postman {//抽象快遞員類
//派送流程
public final void post() {//這里申明為final,不希望子類覆蓋這個(gè)方法鉴嗤,防止更改流程的執(zhí)行順序
prepare();//準(zhǔn)備派送
call();//聯(lián)系收貨人
if (isSign())//是否簽收
sign();//簽收
else refuse();//拒簽
}
protected void prepare() {//準(zhǔn)備操作斩启,固定流程,父類實(shí)現(xiàn)
System.out.println("快遞已達(dá)到醉锅,準(zhǔn)備派送");
}
protected abstract void call();//聯(lián)系收貨人兔簇,聯(lián)系人不一樣,所以為抽象方法硬耍,子類實(shí)現(xiàn)
protected boolean isSign() {//是否簽收,這個(gè)是鉤子方法垄琐,用來控制流程的走向
return true;
}
protected void sign() {//簽收,這個(gè)是固定流程经柴,父類實(shí)現(xiàn)
System.out.println("客戶已簽收狸窘,上報(bào)系統(tǒng)");
}
protected void refuse() {//拒簽,空實(shí)現(xiàn)坯认,這個(gè)也是鉤子方法翻擒,子類可以跟進(jìn)實(shí)際來決定是否去實(shí)現(xiàn)這個(gè)方法
}
}
需要注意的是上面的抽象類(Postman)包含了三種類型的方法:抽象方法、具體方法和鉤子方法牛哺。
- 抽象方法:需要子類去實(shí)現(xiàn)陋气。如上面的
call()
。 - 具體方法:抽象父類中直接實(shí)現(xiàn)引润。如上面的
prepare()
和sign()
巩趁。 - 鉤子方法:有兩種,第一種椰拒,它是一個(gè)空實(shí)現(xiàn)的方法晶渠,子類可以視情況來決定是否要覆蓋它,如上面的
refuse()
燃观;第二種褒脯,它的返回類型通常是boolean類型的,一般用于對(duì)某個(gè)條件進(jìn)行判斷缆毁,如果條件滿足則執(zhí)行某一步驟番川,否則將不執(zhí)行,如上面的isSign()
。
4.2 創(chuàng)建具體實(shí)現(xiàn)類
根據(jù)需要去實(shí)現(xiàn)抽象類中的方法颁督,下面以派送給兩個(gè)不同的人為例践啄,其中一個(gè)簽收,另一個(gè)拒收:
public class PostA extends Postman {//派送給A先生
@Override
protected void call() {//聯(lián)系收貨沉御,實(shí)現(xiàn)父類的抽象方法
System.out.println("聯(lián)系A(chǔ)先生并送到門口");
}
}
public class PostB extends Postman {//派送給B先生
@Override
protected void call() {//聯(lián)系收貨屿讽,實(shí)現(xiàn)父類的抽象方法
System.out.println("聯(lián)系B先生并送到門口");
}
@Override
protected boolean isSign() {//是否簽收,覆蓋父類的鉤子方法,控制流程的走向
return false;
}
@Override
protected void refuse() {//拒簽吠裆,覆蓋父類的鉤子方法
System.out.println("拒絕簽收:商品不符");
}
}
4.3 客戶端測(cè)試
public void test(){
System.out.println("----派送A----");
Postman postA=new PostA();
postA.post();
System.out.println("----派送B----");
Postman postB=new PostB();
postB.post();
}
輸出結(jié)果:
----派送A----
快遞已達(dá)到伐谈,準(zhǔn)備派送
聯(lián)系A(chǔ)先生并送到門口
客戶已簽收,上報(bào)系統(tǒng)
----派送B----
快遞已達(dá)到试疙,準(zhǔn)備派送
聯(lián)系B先生并送到門口
拒絕簽收:商品不符
5. 應(yīng)用場(chǎng)景
- 一次性實(shí)現(xiàn)算法的執(zhí)行順序和固定不變部分诵棵,可變部分則交由子類來實(shí)現(xiàn)。
- 多個(gè)子類中擁有相同的行為時(shí)祝旷,可以將其抽取出來放在父類中履澳,避免重復(fù)的代碼。
- 使用鉤子方法來讓子類決定父類的某個(gè)步驟是否執(zhí)行怀跛,實(shí)現(xiàn)子類對(duì)父類的反向控制距贷。
- 控制子類擴(kuò)展。模板方法只在特定點(diǎn)調(diào)用鉤子方法吻谋,這樣就只允許在這些點(diǎn)進(jìn)行擴(kuò)展储耐。
6. 優(yōu)點(diǎn)
- 提高代碼復(fù)用性,去除子類中的重復(fù)代碼滨溉。
- 提高擴(kuò)展性,不同實(shí)現(xiàn)細(xì)節(jié)放到不同子類中长赞,易于增加新行為晦攒。
7. 缺點(diǎn)
每個(gè)不同的實(shí)現(xiàn)都需要定義一個(gè)子類,這會(huì)導(dǎo)致類的個(gè)數(shù)的增加得哆,設(shè)計(jì)更加抽象脯颜。
8. Android中的源碼分析
Android中View的draw方法就是使用了模板方法模式:
8.1 View的draw方法
public class View{
//鉤子方法,空實(shí)現(xiàn)
protected void onDraw(Canvas canvas) {
}
//鉤子方法贩据,空實(shí)現(xiàn)
protected void dispatchDraw(Canvas canvas) {
}
//繪制方法栋操,定義繪制流程
public void draw(Canvas canvas) {
//其他代碼略
/*
* 繪制流程如下:
*
* 1. 繪制view背景
* 2. 如果有需要,就保存圖層
* 3. 繪制view內(nèi)容
* 4. 繪制子View
* 5. 如果有必要饱亮,繪制漸變框和恢復(fù)圖層
* 6. 繪制裝飾(滑動(dòng)條等)
*/
if (!dirtyOpaque) {
drawBackground(canvas);//步驟1. 繪制view背景
}
// 如果可能的話跳過第2步和第5步(常見情況)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
if (!dirtyOpaque) onDraw(canvas);//步驟3. 繪制view內(nèi)容
dispatchDraw(canvas);//步驟4. 繪制子View
// 覆蓋一部分內(nèi)容矾芙,繪制前景
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
onDrawForeground(canvas); //步驟6. 繪制裝飾(滑動(dòng)條等)
return;
}
}
8.2 說明
-
View
的draw()
方法中定義了一整套的繪制流程苟蹈,這個(gè)流程是固定的寝姿,所有的Android中的View
都是按照這個(gè)流程來繪制的逮走。其中drawBackground()
這個(gè)方法在View
類中是實(shí)現(xiàn)了具體過程的,而onDraw()
方法和dispatchDraw()
方法在View
中都是空實(shí)現(xiàn)殷绍,即都是鉤子方法。不同的子類通過重寫這些空實(shí)現(xiàn)來實(shí)現(xiàn)自身不同的繪制效果谬以。 - 具體的
View
门扇,像TextView
這些單一的View
,就會(huì)重寫onDraw()
方法地淀,由于TextView
沒有子View
失球,所以dispatchDraw()
還是空實(shí)現(xiàn);而ViewGroup
類含有子View
帮毁,需要遍歷子View
并繪制实苞,因此需要重寫onDraw()
和dispatchDraw()
。 - 所以作箍,我們自定義View時(shí)必須且只需重寫
onDraw()
硬梁;自定義ViewGroup
時(shí)則需要重寫onDraw()
和dispatchDraw()
。
8.3 其他
另外胞得,像Activity的生命周期荧止,AsyncTask等等也是用到了模板方法模式,也興趣的可以研究一下阶剑。
相關(guān)文章閱讀
Android的設(shè)計(jì)模式-設(shè)計(jì)模式的六大原則
一句話總結(jié)23種設(shè)計(jì)模式則
創(chuàng)建型模式:
Android的設(shè)計(jì)模式-單例模式
Android的設(shè)計(jì)模式-建造者模式
Android的設(shè)計(jì)模式-工廠方法模式
Android的設(shè)計(jì)模式-簡單工廠模式
Android的設(shè)計(jì)模式-抽象工廠模式
Android的設(shè)計(jì)模式-原型模式
行為型模式:
Android的設(shè)計(jì)模式-策略模式
Android的設(shè)計(jì)模式-狀態(tài)模式
Android的設(shè)計(jì)模式-責(zé)任鏈模式
Android的設(shè)計(jì)模式-觀察者模式
Android的設(shè)計(jì)模式-模板方法模式
Android的設(shè)計(jì)模式-迭代器模式
Android的設(shè)計(jì)模式-備忘錄模式
Android的設(shè)計(jì)模式-訪問者模式
Android的設(shè)計(jì)模式-中介者模式
Android的設(shè)計(jì)模式-解釋器模式
Android的設(shè)計(jì)模式-命令模式
結(jié)構(gòu)型模式:
Android的設(shè)計(jì)模式-代理模式
Android的設(shè)計(jì)模式-組合模式
Android的設(shè)計(jì)模式-適配器模式
Android的設(shè)計(jì)模式-裝飾者模式
Android的設(shè)計(jì)模式-享元模式
Android的設(shè)計(jì)模式-外觀模式
Android的設(shè)計(jì)模式-橋接模式