模板方法

  • 概述
  • UML类图
  • 代码栗子
  • Spring源码体现
  • 总结

概述

  • 概述
    模板方法是一种行为设计模式,它在超类(抽象类)中定义算法的框架,但允许子类覆盖算法的特定步骤而无需更改其结构。

  • 作用

    1. 允许子类实现变化的行为(通过重写钩子方法)
    2. 避免了重复代码:算法的一般工作流程在抽象类的模板方法中实现一次,而子类中实现必要的变体。
    3. 控制允许专业化的点。如果子类只是简单地覆盖模板方法,则它们可以对工作流进行根本性和任意的更改。相反,通过仅覆盖钩子方法,仅可以更改工作流的某些特定细节,并且整个工作流保持不变。

UML类图

img

代码栗子

//动作类
public interface Action {
    /**
     * 吃
     * @param name
     */
    void eat(String name) ;

}
//抽象模板类
public abstract class AbstractDefaultAction implements Action {
    /**
     * 吃饭前的动作
     * 子类必须实现的方法
     */
    public abstract void before();

  //模板方法
    @Override
    public final void eat(String name) {
        before();
        System.out.println("吃饭吃的是:"+name);;
        after();
    }

    /**
     * 吃饭后的动作
     * 钩子方法
     */
    public  void after(){
        System.out.println("默认实现,洗手");
    }
}
public class LiSi extends AbstractDefaultAction {
    private  static String name ="李四";

    @Override
    public void before() {
        System.out.println(name+";吃饭前,不洗手");

    }

    @Override
    public void after() {
        System.out.println("吃完洗碗");
    }
}
public class ZhangSan extends AbstractDefaultAction {
    private  static String name ="张三";

    @Override
    public void before() {
        System.out.println(name+";吃饭前,洗手");

    }
}
  • client
public class Main {
    public static void main(String[] args) {
        Action  a = new ZhangSan() ;
        a.eat("香蕉");
        System.out.println("-----------分割线---------");
        Action  b = new LiSi() ;
        b.eat("苹果");
    }

}
  • 运行效果

img

Spring源码体现

使用IDEA 双击shift查找类,进入org.springframework.context.ConfigurableApplicationContext,找到refresh() 方法,找到他的实现类org.springframework.context.support.AbstractApplicationContext,它的实现方式就是一个模板方法,源码如下:

@Override
public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // 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) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

根据这个refresh的方法注释可知,这就是Spring加载资源配置文件用的,规定了整个加载流程,但是放开了流程细节交由其子类实现

总结

  • 多个子类有公有的方法,并且逻辑基本相同时。把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
  • 重构时,把相同的代码抽取到父类中,然后通过钩子函数约束其行为。
posted @ 2019-11-20 15:02  tanoak  阅读(163)  评论(0编辑  收藏  举报