设计模式之模板方法模式:

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤

简而言之就是:父类定义了骨架(调用哪些方法及其顺序),某些特定的方法由具体的子类来实现。
所以呢?在父类模板方法中是有两类方法的:

  1. 共同的方法:所有子类都必须用到的方法,类似于定义了一个框架,通常不用abstract而是final来修饰,防止子类重写,像下面的play方法
  2. 不同的方法:子类需要重写或者覆盖的方法,这种方法也分为两种:
    1). 抽象方法:父类是抽象方法,子类必须实现的,如下面的startGame,endGame方法;
    2). 钩子方法:父类中是一个空方法,子类继承的时候默认也是空的,这个方法有什么用呢?子类可以通过这个方法来自定义自己的其他一些步骤和过程,从而达到控制父类的目的。

我们将创建一个定义操作的 Game 抽象类,其中,模板方法设置为 final,这样它就不会被重写。BasketballGame和 FootballGame 是扩展了 Game 的实体类,它们重写了抽象类的方法。

1. 创建抽象类Game:

public abstract  class Game {abstract void startGame();abstract void endGame();abstract void init();final void play(){init();startGame();endGame();}
}

2. 创建扩展了上述类的实体类:

public class FootballGame extends Game {@Overridevoid startGame() {System.out.println("start football");}@Overridevoid endGame() {System.out.println("end football");}@Overridevoid init() {System.out.println("init football");}
}
public class BasketballGame extends Game {@Overridevoid startGame() {System.out.println("start basketball");}@Overridevoid endGame() {System.out.println("end basketball");}@Overridevoid init() {System.out.println("init basketball");}
}

3. 使用测试类测试:

public class TemplMain {public static void main(String[] args) {Game game = new FootballGame();//这样如果你想要改变其他球类,只需要换成下面这种形式,其他不用改变//Game game = new BasketballGame();game.play();}
}

再升级一下,举另一个例子:


接口描述的是一种共性,一种动作action;
抽象类描述的是一个模板,一个特定的过程;
而子类则是可以根据自己的需要定制自己的过程。

不过,这样做的好处又是什么呢?

  1. 将族群进行隔离,像上面的图一样,我们可以将不同种类的人,美洲人,亚洲人进行隔离,而不互相影响;
  2. 可以将一些日志和共性的动作很好的分离,规范子类的动作。

其实我们在一些框架,如spring,mybatis等都可以看到模板方法模式的影子,下面简单举一下例子:
像mybatis的Executor接口:

你就可以很明显的看到这种模式:

spring中的模板方法模式:

spring模板方法我来摘抄一些重点给大家看看:
下面的代码展示了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 DefaultResourceLoaderimplements 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,看看他们在类中的定义
看看获取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;

3、具体实现的子类,实现了抽象方法getBeanFactory的子类有:

AbstractRefreshableApplicationContext:

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {@Overridepublic 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;}}
}

GenericApplicationContext:

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {@Overridepublic final ConfigurableListableBeanFactory getBeanFactory() {//同样这里的this.beanFactory在另一个抽象方法中设置        return this.beanFactory;}
}

大家有空可以自己进去看看里面的源码!里面真是高深莫测!

一篇博客读懂设计模式之---模板方法模式相关推荐

  1. 教你如何一篇博客读懂设计模式之—--原型模式

    教你如何一篇博客读懂设计模式之----原型模式 what:是什么 原型模式: 用于创建重复的对象,既不用一个属性一个属性去set和get,又不影响性能,原型模式产生的对象和原有的对象不是同一个实例,他 ...

  2. 教你如何一篇博客读懂设计模式之—--工厂模式

    一篇博客读懂设计模式之-工厂模式 工厂模式在我们日常开发的时候经常用到,相信大家都有了一定的了解,工厂模式是一种创建对象的设计模式,它提供一种创建对象的最佳方式. 主要过程是: 定义一个创建对象的接口 ...

  3. 一篇博客读懂设计模式之---委派模式

    一篇博客读懂设计模式之-委派模式 委派模式可能大家听起来不太熟悉,但是在代码开发的时候却很好用,下面从几个方面来介绍一下 what:是什么? 委派模式:顾名思义,委托其他对象或者实例来帮我们完成任务, ...

  4. 一篇博客读懂设计模式之-----策略模式

    设计模式之策略模式 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的对象 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换. 主要解决:在有多种算法相似的情况下 ...

  5. 一篇博客读懂设计模式之---工厂模式

    设计模式之-工厂模式 工厂模式: 创建过程: 创建Shape接口 public interface Shape {void draw(); } 创建实现类: public class Circle i ...

  6. 一篇博客读懂设计模式之---动态代理与反射

    一篇博客读懂设计模式之---动态代理与反射 先来讲一下反射: 1 关于反射 反射最大的作用之一就在于我们可以不用在编译时就知道某个对象的类型,而在运行时通过提供完整的"包名+类名.class ...

  7. 一篇博客读懂设计模式之---单例模式

    一篇博客读懂设计模式之---单例模式 一.  单例模式 单例对象(Singleton)是一种常用的设计模式.在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在.这样的模式有几个好处 ...

  8. [入门篇]用史上最生动的方式让你一篇博客搞懂Linux进程地址空间,包看包懂!

    目录 0.前言 1.初始程序的地址空间划分 1.1程序地址空间图解 1.2程序地址空间区域划分验证 1.3 程序地址空间小补充 1.4 引入进程地址空间 *2. 两个生动的例子理解进程地址空间 2.1 ...

  9. Linux - 一篇带你读懂 Curl Proxy 代理模式

    curl 是一个很有名的处理网络请求的 类Unix 工具.出于某种原因,我们进行网络请求,需要设置代理.本文讲全面介绍如何为 curl 设置代理 设置代理参数 基本用法 -x, --proxy [pr ...

最新文章

  1. 不是“老赖”是“真还”!罗永浩 6 亿债务还了 4 亿
  2. 老话题,不要在遍历容器中增删容器数据
  3. 错误消息“禁止您没有访问此服务器上的权限/”(关闭)
  4. BZOJ4105 THUSC2015平方运算(线段树)
  5. 常用排序算法之——堆排序
  6. linux mount文件夹
  7. SAP cloud platform + 504 gateway time out Cloud connector
  8. 我们生活在最好的时代
  9. 三个水桶(看了三遍,想了五遍!)
  10. TLStorm:APC UPS 存在零点击0day,可远程烧毁设备、切断电源
  11. iOS 谁说程序猿不懂浪漫之 爱心
  12. 使用servlet实现果树管理系统功能实现,小项目详解,点击链接,可以获得全部源代码
  13. 实现同比、环比计算的N种姿势
  14. Qt编写安防视频监控系统61-子模块5设备控制
  15. 【笔记】Python开发工程师要求摘录
  16. 导数求函数最大值和最小值
  17. 油库可视化指挥调度解决方案
  18. EC读取风扇转速并在BIOS中显示
  19. 项目周期一般多久_股票解套的时间周期一般多久 股票解套要多长时间
  20. 三菱支持c语言的plc,三菱plc编程用什么语言比较好?三菱编程语言的特点

热门文章

  1. java treemap_Java TreeMap pollFirstEntry()方法与示例
  2. Java SimpleTimeZone toString()方法与示例
  3. android中requestFocus 以及与setFocusable的区别
  4. linux——两个客户端之间实现聊天(TCP、单线程)
  5. 使用ACME部署生成阿里云免费HTTPS证书
  6. android 垂直的开关_安卓布局:如何让这两个按钮水平垂直居中
  7. NodeJS知识汇总
  8. e300氛围灯哪里调节_让快乐来得更简单!体验新宝骏E300/E300 PLUS
  9. linux+基因组字符替换,liftover基因组版本直接的coordinate转换
  10. python线程等待_python3 中 Event.wait 多线程等待