今天股市大跌窘游,心情不爽,但是還是賺了點(diǎn)跳纳;也沒心情再研究新的設(shè)計(jì)模式忍饰,明日繼續(xù);就把上次被簡(jiǎn)書鎖定的文章重新發(fā)表一下寺庄;
我們來看模板方法模式:
模板方法模式定義:
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.(定義一個(gè)操作中的算法的框架艾蓝,而將一些步驟延遲到子類中。使得子類可以不改
變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟斗塘。)
模板方法模式其實(shí)大家工作學(xué)習(xí)中一直在使用赢织,只是你不知道你用的就是模板方法模式罷了,就像令狐少俠練就了易經(jīng)筋神功卻不知道練的就是這絕世內(nèi)功一樣馍盟,呵呵其實(shí)沒有系統(tǒng)玩一遍之前我也不知道于置。Ok,我們寫個(gè)Demo,等下你就會(huì)明白我在說什么:
現(xiàn)在我們要造一把槍的模型生產(chǎn)一些槍來打飛機(jī): 我們定義個(gè)模板
package com.ldl.method;
//步槍模板
public abstract class AbstractRifleModel {
// 能上子彈
protected abstract void start();
// 發(fā)射
protected abstract void play();
// 發(fā)出崩繃得一聲響
protected abstract void alarm();
// 熄火了
protected abstract void stop();
/**
* 開槍
*/
public void run() {
this.start();
this.play();
this.alarm();
this.stop();
}
}
如上所述,我們?cè)煲粋€(gè)步槍模板贞岭,來生產(chǎn)不同型號(hào)的步槍八毯;接下來我們生產(chǎn)兩種型號(hào)的92式和一款A(yù)K狙擊步槍:
/**
* 造一個(gè)92式步槍
*
* @author Administrator
*
*/
public class RifleFor92Model extends AbstractRifleModel {
@Override
protected void start() {
// TODO Auto-generated method stub
System.out.println("92式子彈上膛");
}
@Override
protected void play() {
// TODO Auto-generated method stub
System.out.println("92式發(fā)射......");
}
@Override
protected void alarm() {
// TODO Auto-generated method stub
System.out.println("92式發(fā)出轟隆一聲響......");
}
@Override
protected void stop() {
// TODO Auto-generated method stub
System.out.println("92式熄火......");
}
}
下面是一部AK狙擊步槍.
/**
* 造一個(gè)Ak47
*
* @author Administrator
*
*/
public class Ak47RifleModel extends AbstractRifleModel {
@Override
protected void start() {
System.out.println("Ak47子彈上膛");
}
@Override
protected void play() {
System.out.println("Ak47發(fā)射");
}
@Override
protected void alarm() {
// TODO Auto-generated method stub
System.out.println("Ak47發(fā)出轟隆一聲響......");
}
@Override
protected void stop() {
// TODO Auto-generated method stub
System.out.println("Ak47熄火......");
}
}
下面我們測(cè)試一下搓侄,看行不行
package com.ldl.method;
public class TestRifle {
public static void main(String[] args) {
// TODO Auto-generated method stub
AbstractRifleModel ak47Rifle = new Ak47RifleModel();
AbstractRifleModel riflefor92 = new RifleFor92Model();
ak47Rifle.run();
riflefor92.run();
}
}
效果還不錯(cuò)哦;
哈哈话速,這種寫法是不是很常見呢讶踪?比如我們做架構(gòu)Base層抽取的時(shí)候,我們抽取共性到Base層泊交,由子類具體實(shí)現(xiàn)乳讥,Base中調(diào)用;這就是模板方法模式活合;抽象方法就是一般方法雏婶;抽象類中的具體實(shí)現(xiàn)也就是模板方法;例子中的run方法;
Ok ,現(xiàn)在我們擴(kuò)展一下:打飛機(jī)倒是可以白指,要是打鳥怎么辦留晚,每次都轟隆一聲響還不把鳥嚇跑了,我們現(xiàn)在要的是想讓它響就響告嘲,不想讓它響就不響错维;給槍裝個(gè)消聲器不就行了;好了我們來改造一下看看:
在模板里添加一個(gè)方法:
// 是否添加消聲器
protected boolean isAlarm() {
return true;
}
改造一下發(fā)射的方法
/**
* 開槍
*/
public void run() {
this.start();
this.play();
if (isAlarm()) {
this.alarm();
}
this.stop();
}
我們跑一下:
果然92式步槍安裝上消聲器后不會(huì)發(fā)出聲響了橄唬;棒棒噠赋焕;
下面我們總結(jié)一下模板方法模式:
缺點(diǎn):
按照我們的設(shè)計(jì)習(xí)慣,抽象類負(fù)責(zé)聲明最抽象仰楚、最一般的事物屬性和方法隆判,實(shí)現(xiàn)類完成
具體的事物屬性和方法。但是模板方法模式卻顛倒了僧界,抽象類定義了部分抽象方法侨嘀,由子類
實(shí)現(xiàn),子類執(zhí)行的結(jié)果影響了父類的結(jié)果捂襟,也就是子類對(duì)父類產(chǎn)生了影響咬腕,這在復(fù)雜的項(xiàng)目
中,會(huì)帶來代碼閱讀的難度葬荷,
優(yōu)點(diǎn):
行為由父類控制涨共,子類實(shí)現(xiàn)
基本方法是由子類實(shí)現(xiàn)的,因此子類可以通過擴(kuò)展的方式增加相應(yīng)的功能宠漩,符合開閉原
則举反。
提取公共部分代碼,便于維護(hù):
如果我們不抽取到父類中扒吁,任由這種散亂
的代碼發(fā)生照筑,維護(hù)人員為了修正一個(gè)缺陷,需要到處查找類似的代
碼!記住公共代碼要抽取
封裝不變部分凝危,擴(kuò)展可變部分:
包括我們使用第三方庫(kù)也是一樣,我們?nèi)绻胄薷钠湓创a晨逝,最好是繼承擴(kuò)展而非直接修改:
把認(rèn)為是不變部分的算法封裝到父類實(shí)現(xiàn)蛾默,而可變部分的則可以通過繼承來繼續(xù)擴(kuò)展:
注意 抽象模板中的基本方法盡量設(shè)計(jì)為protected類型,符合迪米特法則捉貌,不需要暴露
的屬性或方法盡量不要設(shè)置為protected類型支鸡。實(shí)現(xiàn)類若非必要,盡量不要擴(kuò)大父類中的訪問
權(quán)限趁窃。
使用場(chǎng)景:
多個(gè)子類有公有的方法牧挣,并且邏輯基本相同時(shí);
重要、復(fù)雜的算法醒陆,可以把核心算法設(shè)計(jì)為模板方法瀑构,周邊的相關(guān)細(xì)節(jié)功能則由各個(gè)
子類實(shí)現(xiàn),模板方法一般用final修飾以防子類擴(kuò)展修改核心算法
重構(gòu)時(shí),模板方法模式是一個(gè)經(jīng)常使用的模式刨摩,把相同的代碼抽取到父類中寺晌,然后通
過鉤子函數(shù)(見“模板方法模式的擴(kuò)展”)約束其行為。