一、基本介紹
模板方法模式,在一個抽象類公開定義了執行它的方法的模板。它的子類可以按需要重寫方法實現,但調用將以抽象類中定義的方式進行。
簡單說,就是定義一個操作的算法骨架,而將一些步驟延遲到子類中,使得子類可以不改變一個算法的結構就可以重新定義該算法的某些步驟。
二、模板方法模式的結構
AbstractClass:實現一個模板方法,定義了算法的骨架,具體子類將重定義PrimitiveOperation以實現一個算法的步驟。AbstractClass其實就是一個抽象模板,定義並實現了一個模板方法。這個模板方法一般是一個具體的方法。它給出了一個頂級邏輯的骨架,而邏輯的組成步驟在相應的抽象操作中,推遲到子類實現。頂級邏輯也有可能調用一些具體方法。
ConcreteClasses:實現PrimitiveOperation以完成算法與特定子類相關的步驟。ConcreteClass實現父類所定義的一個或多個抽象方法。每一個AbstractClass都可以有任意多個ConcreteClass與之對應,而每一個ConcreteClass都可以給出這些抽象方法(也就是頂級邏輯的組成步驟)的不同實現,從而使得頂級邏輯的實現各不相同。
三、模板方法模式的優缺點
1、優點
(1)模板方法模式通過把不變的行爲搬移到父類,去除了子類中的重複代碼。
(2)子類實現算法的某些細節,有助於算法的擴展。
(3)通過一個父類調用子類實現的操作,通過子類擴展增加新的行爲,符合“開放-封閉原則”。
2、缺點
按照設計習慣,抽象類負責聲明最抽象、最一般的事物屬性和方法,實現類負責完成具體的事務屬性和方法,但是模板方式正好相反,子類執行的結果影響了父類的結果,會增加代碼閱讀的難度。
四、模板方法模式的使用場景
1、多個子類有共有的方法,並且邏輯基本相同。
2、重要、複雜的算法,可以把核心算法設計爲模板方法,周邊的相關細節功能則由各個子類實現。
3、重構時,模板方法是一個經常使用的方法,把相同的代碼抽取到父類中,然後通過構造函數約束其行爲。
五、鉤子方法
1、在模板方法模式的父類中,我們可以定義一個方法,它默認不做任何事,子類可以視情況覆蓋它,該方法稱爲鉤子方法。
六、代碼實例
1、抽象類
package com.guor.template;
public abstract class SoyaMilk {
//模板方法, make , 模板方法可以做成final , 不讓子類去覆蓋.
final void make() {
select();
addCondiments();
soak();
beat();
}
//選材料
void select() {
System.out.println("第一步:選擇好的新鮮黃豆 ");
}
//添加不同的配料, 抽象方法, 子類具體實現
abstract void addCondiments();
//浸泡
void soak() {
System.out.println("第三步, 黃豆和配料開始浸泡, 需要3小時 ");
}
void beat() {
System.out.println("第四步:黃豆和配料放到豆漿機去打碎 ");
}
}
2、花生類
package com.guor.template;
public class PeanutSoyaMilk extends SoyaMilk {
@Override
void addCondiments() {
System.out.println(" 加入上好的花生 ");
}
}
3、紅豆類
package com.guor.template;
public class RedBeanSoyaMilk extends SoyaMilk {
@Override
void addCondiments() {
System.out.println(" 加入上好的紅豆 ");
}
public static void main(String[] args) {
//製作紅豆豆漿
System.out.println("----製作紅豆豆漿----");
SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk();
redBeanSoyaMilk.make();
System.out.println("----製作花生豆漿----");
SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
peanutSoyaMilk.make();
}
}
4、控制檯輸出
七、SpringIOC中的模板方法模式
Spring中幾乎所有的擴展都是用了模板方法模式,以SpringIOC爲例簡單說明一下。
1、首先定義一個接口ConfigurableApplicationContext,聲明模板方法refresh。
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
/**聲明瞭一個模板方法*/
void refresh() throws BeansException, IllegalStateException;
}
2、抽象類AbstractApplicationContext實現了接口
主要實現了模板方法refresh(這個方法很重要,是各種IOC容器初始化的入口)的邏輯。
/**
* 模板方法的具體實現
*/
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
//注意這個方法是,裏面調用了兩個抽象方法refreshBeanFactory、getBeanFactory
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
//注意這個方法是鉤子方法
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
//注意這個方法是鉤子方法
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
這裏最主要有一個抽象方法obtainFreshBeanFactory、兩個鉤子方法postProcessBeanFactory和onRefresh,看看他們在類中的定義
兩個鉤子方法
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}
protected void onRefresh() throws BeansException {
// For subclasses: do nothing by default.
}
再看看獲取Spring容器的抽象方法:
/**其實他內部只調用了兩個抽象方法**/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
具體要取那種BeanFactory容器的決定權交給了子類!
3、AbstractRefreshableApplicationContext
AbstractRefreshableApplicationContext是實現了抽象方法getBeanFactory的子類;
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
synchronized (this.beanFactoryMonitor) {
if (this.beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed - " +
"call 'refresh' before accessing beans via the ApplicationContext");
}
//這裏的this.beanFactory在另一個抽象方法refreshBeanFactory的設置的
return this.beanFactory;
}
}
}
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
//同樣這裏的this.beanFactory在另一個抽象方法中設置
return this.beanFactory;
}
}
其實這裏的差別還不是很大,我們可以看看另一個抽象方法refreshBeanFactory的實現,兩個抽象方法的配合使用。