解釋
定義一個操作中的算法的框架,而將一些步驟延遲到子類中。使得子類可以不改變一個算法的結(jié)構(gòu)可重定義改算法的某些特定步驟(通俗理解:將方法的實現(xiàn)延遲到子類)
類圖

應(yīng)用場景
- 多個子類有公有的方法,并且邏輯基本相同
- 重要、復(fù)雜的算法,可以把核心算法設(shè)計為模版方法,周邊的相關(guān)細(xì)節(jié)功能則由各個子類實現(xiàn)
- 重構(gòu)時,模板方法模式是一個經(jīng)常使用的模式,把相同的代碼抽取到父類中,然后通過鉤子函數(shù)(見下文解釋)約束其行為
寫法
- 聲明抽象父類,抽象公有方法
- 延遲到子類實現(xiàn)抽象方法
- 通過鉤子函數(shù)約束父親行為(可選)
通用代碼
/**
* Created by zs on 2017/3/17.
*
* 抽象模板類
*/
public abstract class AbstractClass {
//基本方法
protected abstract void doSomething();
//基本方法
protected abstract void doAnything();
public void templateMethod(){
//調(diào)用基本方法,完成相關(guān)邏輯
this.doAnything();
this.doSomething();
}
}
/**
* Created by zs on 2017/3/17.
*
* 具體模板類
*/
public class ConcreteClass1 extends AbstractClass {
@Override
protected void doSomething() {
//to do you work ...
}
@Override
protected void doAnything() {
//to do you work ...
}
}
/**
* Created by zs on 2017/3/17.
*
* 具體模板類
*/
public class ConcreteClass2 extends AbstractClass {
@Override
protected void doSomething() {
//to do you work ...
}
@Override
protected void doAnything() {
//to do you work ...
}
}
/**
* Created by zs on 2017/3/17.
*
* 場景類
*/
public class Client {
public static void main(String[] args) {
AbstractClass class1 = new ConcreteClass1();
AbstractClass class2 = new ConcreteClass2();
//調(diào)用模板方法
class1.templateMethod();
class2.templateMethod();
}
}
多說一句
抽象模板中的基本方法盡量設(shè)計為protected類型,符合迪米特法則(請看下文),不需要暴露的屬性和方法盡量不要設(shè)置為protected類型,實現(xiàn)類若非必要,盡量不要擴(kuò)大父類中的訪問權(quán)限
那么什么是迪米特法則(LoD)呢:
通俗理解:一個類應(yīng)該對自己需要耦合或調(diào)用的類知道的最少,你(被耦合或調(diào)用的類)的內(nèi)部是如何復(fù)雜都和我沒關(guān)系,那是你的事情,我就知道你提供的這么多的public 方法,我就調(diào)用這么多,其他的我一概不關(guān)系。
解釋分析:
- 只和朋友交流 一個類只和朋友交流,不與陌生類交流
- 朋友間也是有距離的,迪米特法則要求類 “羞澀” 一點,盡量不要對外公布太多的public方法和非靜態(tài)的public變量,盡量內(nèi)斂
- 是自己的就是自己的,如果一個方法放在本類中,既不增加類間關(guān)系,也對本類不產(chǎn)生負(fù)面影響,那就放在本類中
擴(kuò)展
問題:父類的模板方法約定了基本方法(父類抽象方法)的執(zhí)行,但是子類無法私人訂制是否要執(zhí)行某一個基本方法?
解決方案:鉤子方法
鉤子方法:影響模板方法執(zhí)行結(jié)果的方法
一言不合上代碼:
/**
* Created by zs on 2017/3/17.
*
* 抽象模板類
*/
public abstract class AbstractClass {
//基本方法
protected abstract void doSomething();
//基本方法
protected abstract void doAnything();
//鉤子方法
protected boolean isExecuteSomething(){
return true;
}
//模板方法
public void templateMethod(){
//調(diào)用基本方法,完成相關(guān)邏輯
this.doAnything();
//是否執(zhí)行,由子類決定
if(isExecuteSomething()){
this.doSomething();
}
}
}
/**
* Created by zs on 2017/3/17.
*
* 具體模板類
*/
public class ConcreteClass1 extends AbstractClass {
@Override
protected void doSomething() {
// to do you work...
}
@Override
protected void doAnything() {
//to do you work ...
}
@Override
protected boolean isExecuteSomething() {
return false;
}
}
/**
* Created by zs on 2017/3/17.
* * 具體模板類
*/
public class ConcreteClass2 extends AbstractClass {
@Override
protected void doSomething() {
//to do you work ...
}
@Override
protected void doAnything() {
//to do you work ...
}
}
曲線救國
問題:父類怎么調(diào)用子類的方法?能?不能?
回答:能
解決方案:
- 把子類傳遞到父類的有參構(gòu)造中,然后調(diào)用
- 使用反射的方式調(diào)用(你使用了反射還有誰不能差遣的,還有誰......還有誰......)
- 父類調(diào)用子類的靜態(tài)方法
自嘲:這三種方法都是直接調(diào)用子類的方法,項目中允許使用嗎?不允許!為什么要用父類調(diào)用子類的方法,如果一定要調(diào)用子類,那為什么要繼承它呢?
換個姿勢:父類建立框架,子類重寫了父類部分的方法后,再調(diào)用父類繼承的方法,產(chǎn)生不同的結(jié)果,這一修改子類,影響父類的行為,曲線救國的方式實現(xiàn)了父親依賴子類的場景。
一言不合再上代碼:
/**
* Created by zs on 2017/3/17.
*
* 抽象模板類
*/
public abstract class AbstractClass {
//基本方法
protected abstract void doSomething();
//基本方法
protected abstract void doAnything();
//模板方法
public void templateMethod(){
//調(diào)用基本方法,完成相關(guān)邏輯
this.doAnything();
this.doSomething();
}
}
/**
* Created by zs on 2017/3/17.
*
* 具體模板類
*/
public class ConcreteClass1 extends AbstractClass {
@Override
protected void doSomething() {
// to do you work...
//父類調(diào)用子類
sonMethod();
//to do you work...
}
@Override
protected void doAnything() {
//to do you work ...
}
//子類方法
private void sonMethod(){
// to do you work
}
}
優(yōu)缺點
優(yōu)點:
- 封裝不變部分,擴(kuò)展可變部分
- 提取公共部分代碼,便于維護(hù)
- 行為由父類控制,子類實現(xiàn)
缺點:
子類執(zhí)行的結(jié)果影響了父類的結(jié)果,也就是子類產(chǎn)生了影響,這在復(fù)雜的項目中,會帶來代碼閱讀的難度