基本概念

什么是模板方法(Template method):父类定义了骨架(调用哪些方法及顺序),某些特定方法由子类实现。

最大的好处:代码复用,减少重复代码。除了子类要实现的特定方法,其他方法及方法调用顺序都在父类中预先写好了。

所以父类模板方法中有两类方法:

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

设计模式——Spring IoC中用到的模板方法模式的更多相关文章

  1. Android设计模式(八)--模板方法模式

    到国美面试Android的时候.问我的设计模式相关的问题: 1.单例模式的意义时什么. 2.有哪几种工厂方法模式: 3.你用过的模板方法模式.举例说明: 自己感觉答的一塌糊涂. 模板方法模式都没说出来 ...

  2. 设计模式(二十二)模板方法模式 Template

    泡茶?泡咖啡? 我们用泡茶和泡咖啡两种行为来引入这一设计模式. 思考一下“泡茶”的过程: 煮水 -> 用沸水泡茶叶 -> 把茶倒进杯子 -> 放点柠檬之类的佐料. 然后再看一下“泡咖 ...

  3. 折腾Java设计模式之模板方法模式

    博客原文地址:折腾Java设计模式之模板方法模式 模板方法模式 Define the skeleton of an algorithm in an operation, deferring some ...

  4. 应聘阿里,字节跳动,美团必须掌握的Spring IOC与工厂模式

    Spring IOC与工厂模式 PS:本文内容较为硬核,需要对java的面向对象.反射.类加载器.泛型.properties.XML等基础知识有较深理解. (一)简单介绍 在讲Spring IOC之前 ...

  5. 设计模式 - 模板方法模式详解及其在Spring中的应用

    基本介绍 模板方法模式(Template Method Pattern)也叫模板模式,它在一个抽象类中公开定义了执行它的方法的模板,它的字类可以按需重写方法实现,但调用将以抽象类中定义的方式进行. 简 ...

  6. 设计模式 : Template method 模板方法模式 -- 行为型

      设计模式中,模板模式面向的是方法级别的流程.(不过好像世界上大部分问题,都可以抽象点.抽象点吧,最后抽象到一个方法里面吧.) 1. 一个方法,可以用来描述一个流程,这个流程涉及多个环节,不同环节可 ...

  7. [.net 面向对象程序设计深入](26)实战设计模式——使用Ioc模式(控制反转或依赖注入)实现松散耦合设计(1)

    [.net 面向对象程序设计深入](26)实战设计模式——使用IoC模式(控制反转或依赖注入)实现松散耦合设计(1) 1,关于IOC模式 先看一些名词含义: IOC: Inversion of con ...

  8. javascript设计模式——模板方法模式

    前面的话 在javascript开发中用到继承的场景其实并不是很多,很多时候喜欢用mix-in的方式给对象扩展属性.但这不代表继承在javascript里没有用武之地,虽然没有真正的类和继承机制,但可 ...

  9. [.net 面向对象程序设计深入](31)实战设计模式——使用Ioc模式(控制反转或依赖注入)实现松散耦合设计(1)

    [.net 面向对象程序设计深入](31)实战设计模式——使用IoC模式(控制反转或依赖注入)实现松散耦合设计(1) 1,关于IOC模式 先看一些名词含义: IOC: Inversion of con ...

随机推荐

  1. opcode的执行

    原文链接:http://www.orlion.ga/1001/ 当.php文件被编译为opcode后,下一步的执行并非是把opcode编译为机器码而是类似于如下的方式执行: while (TRUE)  ...

  2. linux下查看进程占用端口和端口占用进程命令

    Linux下查看进程占用端口: 查看程序对应进程号:ps –ef|grep 进程名 REDHAT :查看进程号所占用的端口号:netstat –nltp|grep 进程号 ubuntu:查看进程占用端 ...

  3. Linux - Tips

    01 - Linux的系统结构 Linux 系统结构详解 02 - 执行多条命令 方法1:在命令行下可以一次性粘贴多条语句,shell会依次执行并输出结果 方法2:在一个命令行中,用分号将各个命令隔开 ...

  4. Spring MVC异常处理详解

    Spring MVC中异常处理的类体系结构 下图中,我画出了Spring MVC中,跟异常处理相关的主要类和接口. 在Spring MVC中,所有用于处理在请求映射和请求处理过程中抛出的异常的类,都要 ...

  5. [SDK2.2]Windows Azure Virtual Network (4) 创建Web Server 001并添加至Virtual Network

    <Windows Azure Platform 系列文章目录> 在上一章内容中,笔者已经介绍了以下两个内容: 1.创建Virtual Network,并且设置了IP range 2.创建A ...

  6. 精品资源:40个实用的 PSD 贴纸模板《下篇》

    贴纸经常被网页设计师用于为标注网站中的不同元素,以此吸引访客的注意.此外,使用贴纸也使网站更具吸引力.虽然设计者可以使用 Photoshop 制作贴纸,但他们也可以利用从互联网上下载现成的模板.这些模 ...

  7. JS魔法堂:再次认识Function.prototype.call

    一.前言                                大家先预计一下以下四个函数调用的结果吧! var test = function(){ console.log('hello w ...

  8. C# 目录(文件夹)复制实现

    private static void CopyDir(DirectoryInfo origin, string target) { if (!target.EndsWith("\\&quo ...

  9. eclipse设置及快捷键

    快捷键 查看所有快捷键: Ctrl+Shift+L 调试代码: F11 逐语句: F5 逐过程: F6 快速执行代码: Ctrl+F11 自动格式化代码: Ctrl+Shift+F 在本行代码下插入新 ...

  10. Type 'Insus.NET.PictureObject' in Assembly 'App_Code, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.

    昨晚想实现一个功能,需要把一个对象存储于ViewState中去,但在运行时,出现下面的异常. Type 'Insus.NET.PictureObject' in Assembly 'App_Code, ...