基本介紹
模板方法模式(Template Method Pattern)也叫模板模式,它在一個抽象類中公開定義了執行它的方法的模板,它的字類可以按需重寫方法實現,但調用將以抽象類中定義的方式進行。
簡單來說,模板方法模式定義一個操作中的算法的骨架,將一些步驟延遲到子類中,使得子類可以不改變一個算法的結構,就能重新定義該算法的某些特定步驟
模式結構

-
AbstractClass:抽象類,類中實現了模板方法,定義了算法的骨架
-
ConcreteClass:實現類,繼承抽象類,重寫其中的抽象方法
舉例說明
編寫一個制作豆漿的程序
制作豆漿的流程:選材(黃豆) → 添加配料 → 浸泡 → 磨豆漿
要求:通過添加不同的配料,可以制作出不同口味的豆漿(除了添加配料不同,其它步驟是相同的)
1、定義一個抽象類,BaseSoyMilk:豆漿類
public abstract class BaseSoyMilk {
/**
* 模板方法,制作豆漿的流程
* 聲明為 final,防止子類重寫
*/
protected final void make() {
select();
addOther();
soak();
grind();
}
private void select() {
System.out.println("選擇上好的黃豆");
}
protected abstract void addOther();
private void soak() {
System.out.println("浸泡原材料");
}
private void grind() {
System.out.println("研磨成豆漿");
}
}
2、創建子類,PeanutSoyMilk:花生豆漿
public class PeanutSoyMilk extends BaseSoyMilk {
@Override
protected void addOther() {
System.out.println("添加花生");
}
}
RedBeanSoyMilk:紅豆豆漿
public class RedBeanSoyMilk extends BaseSoyMilk {
@Override
protected void addOther() {
System.out.println("添加紅豆");
}
}
3、創建測試類:Client
public class Client {
@Test
public void test() {
BaseSoyMilk peanutSoyMilk = new PeanutSoyMilk();
peanutSoyMilk.make();
System.out.println("****************");
BaseSoyMilk redBeanSoyMilk = new RedBeanSoyMilk();
redBeanSoyMilk.make();
}
}
4、運行結果
選擇上好的黃豆
添加花生
浸泡原材料
研磨成豆漿
************
選擇上好的黃豆
添加紅豆
浸泡原材料
研磨成豆漿
鈎子方法
在模板方法模式的抽象類中,我們可以定義一個方法,它默認不做任何事,子類可以視情況來決定是否重寫它,該方法稱為「鈎子」。
依然以上面制作豆漿的例子來說明,如果我們什么配料都不想加,制作純豆漿,可以使用鈎子方法對其改造。
1、為 BaseSoyMilk
類添加一個方法 isAdd
判斷是否添加其它材料
public abstract class BaseSoyMilk {
/**
* 模板方法,制作豆漿的流程
* 聲明為 final,防止子類重寫
*/
protected final void make() {
select();
if(isAdd()){
addOther();
}
soak();
grind();
}
private void select() {
System.out.println("選擇上好的黃豆");
}
protected abstract void addOther();
private void soak() {
System.out.println("浸泡原材料");
}
private void grind() {
System.out.println("研磨成豆漿");
}
protected boolean isAdd(){
return true;
}
}
2、創建一個子類 PureSoyMilk:純豆漿
public class PureSoyMilk extends BaseSoyMilk {
@Override
protected void addOther() {
}
@Override
protected boolean isAdd() {
return false;
}
}
3、測試
@Test
public void test2() {
BaseSoyMilk pureSoyMilk = new PureSoyMilk();
pureSoyMilk.make();
}
4、運行結果(現在就沒有添加其它材料的步驟了)
選擇上好的黃豆
浸泡原材料
研磨成豆漿
模式分析
🎉 優點
- 算法只存在於父類中,易修改
- 實現了代碼的復用性,父類的一些方法直接被子類使用
- 既統一了算法,也保證了很大的靈活性
💣 缺點
- 每一個不同的實現都需要一個子類來實現,導致類的數量增加,是系統更龐大
🎯 注意事項
- 一般模板方法要加上 final 修飾符,防止子類重寫
🎲 使用場景
- 當我們要完成某個過程,該過程有一系列步驟,而且這些步驟基本相同,只有個別步驟的實現可能不同,可以考慮使用模板方法模式
在 Spring 源碼中的應用
模板方法模式在 Spring IOC 容器初始化的時候有所應用,我們看一下 AbstractApplicationContext
中的 refresh
方法,它就是一個模板方法,里面調用了一系列方法,有以實現的具體方法,有未實現的抽象方法,也有空的鈎子方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
//此方法內部調用了兩個抽象方法refreshBeanFactory()和getBeanFactory()
//具體要取哪種beanFactory的控制權交給了子類
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
//鈎子方法
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
//鈎子方法
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}
部分結構類圖如下