7、【設計模式】模版模式
出現場景
檔案系統目錄載入配置檔案(FileSystemXmlApplicationContext),類路徑載入配置檔案(ClassPathXmlApplicationContext),以及根據專案上下文目錄(XmlWebApplicationContext)載入配置檔案。載入的過程模版方法:AbstractApplicationContext.refresh()
jdbcTemplate
基本概念
父類定義了骨架(呼叫哪些方法及順序),某些特定方法由子類實現。
最大的好處:程式碼複用,減少重複程式碼。除了子類要實現的特定方法,其他方法及方法呼叫順序都在父類中預先寫好了。
所以父類模板方法中有兩類方法:
1、共同的方法:所有子類都會用到的程式碼
2、不同的方法:子類要覆蓋的方法,分為兩種:
A、抽象方法:父類中的是抽象方法,子類必須覆蓋
B、鉤子方法:父類中是一個空方法,子類繼承了預設也是空的
注:為什麼叫鉤子,子類可以通過這個鉤子(方法),控制父類,因為這個鉤子實際是父類的方法(空方法)!
模板方法模式,和現實中的模板很像,一個文件的模板通常是一個完成了部分內容的表格(表格模板就像一個模板方法),每個人都會拿到表格的副本(具體的實現類)進行某些項的填寫,每個人都可以對指定項(抽象方法或鉤子方法)進行填寫,表格中的必填項就像抽象方法必須實現,表格中的非必填項就是鉤子方法。當然只是比喻和實際情況不完全一樣。
UML圖
Java程式碼展示
下面的程式碼展示了,模板方法模式在Java程式碼中通常是怎樣的:
1、先定義一個介面,主要是定義了模板方法
public interface TemplateInterface { public void execute(); }
2、抽象類實現了介面,主要是實現了模板方法的邏輯,模板方法中呼叫了自己的邏輯方法,還有最重要的鉤子方法和抽象方法
public abstract class TemplateAbstractClass implements TemplateInterface{ /**模板方法*/ @Override public void execute() { preDoSomething(); abstractMethod(); hookMethod(); afterDoSomething(); } private void preDoSomething(){ System.out.println("before do some thing in abstract class"); } private void afterDoSomething(){ System.out.println("after do some thing in abstract class"); } /**抽象方法*/ public abstract void abstractMethod(); /**鉤子方法*/ public void hookMethod(){ } }
3、兩個子類,One只實現了抽象方法,Two實現了抽象方法並覆蓋了鉤子方法
public class SubClassOne extends TemplateAbstractClass{ /**抽象方法*/ @Override public void abstractMethod() { System.out.println("do another thing by subClassOne"); } }
public class SubClassTwo extends TemplateAbstractClass{ /**抽象方法*/ @Override public void abstractMethod() { System.out.println("do another thing by subClassTwo"); } /**鉤子方法*/ @Override public void hookMethod() { System.out.println("hook method in subClassTwo"); } }
Spring中的模板方法模式
Spring中幾乎所有的擴充套件,都使用了模板方法模式,JdbcTemplate中應該很多,不過還沒學到那裡,這裡說下IoC部分的模板方法模式!
注:貌似在業務系統中很少看到,是開發者的編碼能力問題還是對實際情況不適用,但是在框架中很多,Java IO、Spring、Hibernate等,可能是作為一個框架來說考慮更多的是擴充套件問題!
下面的程式碼展示了Spring IOC容器初始化時運用到的模板方法模式。(擷取部分關鍵程式碼)
1、首先定義一個介面ConfigurableApplicationContext,宣告模板方法refresh
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable { /**聲明瞭一個模板方法*/ void refresh() throws BeansException, IllegalStateException; }
2、抽象類AbstractApplicationContext實現了介面,主要實現了模板方法refresh(這個方法很重要,是各種IOC容器初始化的入口)的邏輯
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext, DisposableBean { /**模板方法的具體實現*/ 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、具體實現的子類,實現了抽象方法getBeanFactory的子類有:
AbstractRefreshableApplicationContext:
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的實現,兩個抽象方法的配合使用。
所以這裡的UML是: