1. 程式人生 > >7、【設計模式】模版模式

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是: