【源码分析设计模式 9】SpringIOC中的模板方法模式

一、基本介绍

模板方法模式,在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。

简单说,就是定义一个操作的算法骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构就可以重新定义该算法的某些步骤。

二、模板方法模式的结构

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的实现,两个抽象方法的配合使用。

猜你喜欢

转载自blog.csdn.net/guorui_java/article/details/106677554