19.严丝合缝的文明——模板方法模式详解

2024-03-24 20:04

本文主要是介绍19.严丝合缝的文明——模板方法模式详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述
“项目评审的节点又快到了,PPT你写了没?”
“Oops,忘了,有模板没?给我一份”

概述

模板,一个频繁出现在办公室各类角色口中的词,它通常意味着统一、高效、经验和优质。各项汇报因为PPT的模板变得更加生动,各式的报告因为有了文档模板变得更加规范。找规律是人类很愿意钻研的工作之一,那么在设计模式中,是否有一种模板能够帮我们解决一些问题呢?
答案是肯定的,有!
在这里插入图片描述


一言

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


炼金术

在这里插入图片描述
为了更好的理解模板模式,我想起了这样一个场景。我们都玩过打怪升级的养成类游戏,拿到了野外的原料再去城里的铁匠铺锻造,不同的材料会合成不同的装备。
如果让你用代码实现这个需求,你有什么样的思路?
我们只要稍加思索就会发现,这个需求中大部分的过程都是不变的,唯一的变数就在于投入的原料,在这样的场景下,模板方法模式就非常契合我们的需求了。


原理

在这里插入图片描述

  1. AbstractClass抽象类,类中实现了模板方法,定义了算法骨架,具体子类需要实现其它抽象方法operation2,3,4;
  2. ConcreteClass实现抽象方法operation2,3,4,以完成算法中特定子类的步骤;

简单实现

铁匠铺

public abstract class BlackSmith {final void make(){fire();putMaterial();hit();}void fire(){System.out.println("锻造炉升温");}abstract void putMaterial();void hit(){System.out.println("开始制作");}
}

铸剑室

public class SwordBlackSmith extends BlackSmith{@Overridevoid putMaterial() {System.out.println("放入铸剑材料:铁矿石");}
}

制弓室

public class ArchBlackSmith extends BlackSmith{@Overridevoid putMaterial() {System.out.println("放入制弓材料:木材,牛筋");}
}

在这里插入图片描述


钩子方法

上面的实现通俗易懂,下面我们思考一下,模板方法中,如果我需要视情况规避掉其中几个方法的执行,应该如何实现?
比如说:”冬天到了,铁匠铺没有生意,但是铁匠很冷,他只想让锻造炉升温,但是不需要放入任何锻造材料“。你有什么思路?
实际上这种需求就可以用钩子方法来实现,下面我们在原来的实现上稍加处理。
铁匠铺(含钩子)

public abstract class BlackSmith {final void make(){fire();if (work()){putMaterial();hit();}}void fire(){System.out.println("锻造炉升温");}abstract void putMaterial();void hit(){System.out.println("开始制作");}boolean work(){return true;}
}

凛冬将至

public class WinterIsComing extends BlackSmith{@Overridevoid putMaterial() {}@Overrideboolean work() {return false;}
}

在这里插入图片描述


IOC源码中模板方法模式的应用

相信刷过Java八股的同学在刚刚读到钩子方法的时候就已经想到了IOC容器初始化的实现了,各种论坛、文档上只要一提IOC过程、容器初始化 必然会说到钩子方法,但是相信很多同学也和笔者起初一样,对这种生涩的描述感觉一头雾水。今天我就带大家把这个听起来很晦涩的描述讲通。
没错,作为设计模式的集大成者,springframework在IOC的源码实现中包含着大量的钩子函数实现,这里也是对模板方法模式的主要应用。
我们先来看框架结构:
在这里插入图片描述
spring源码中,上下文接口ApplicationContext是很靠近底层的一个接口,它也是很重要的一个衔接接口。ConfigurableApplicationContext接口作为ApplicaitonContext的一个分支实现,承担了大量的springIOC初始化工作,而ConfigurableApplicationContext有一个核心的实现类AbstractApplicationContext,在这个实现类中有一个很重要很重要很重要的方法,就是refresh()方法
我愿意称这个方法为spring之梦开始的地方,这个方法就是模板方法模式的典型应用,包含了各种前置后置实现和钩子方法,更是spring生命周期的核心体现。
相关源码

	@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");// 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);StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);beanPostProcess.end();// 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();contextRefresh.end();}}}

在这个方法中,postProcessBeanFactory,onRefresh都是预留的钩子方法,在这里都是空实现。而对于这些钩子的实现往往依赖于更高层的子类,比如说:GenericWebApplicationContext。
相关源码

	/*** Modify the application context's internal bean factory after its standard* initialization. All bean definitions will have been loaded, but no beans* will have been instantiated yet. This allows for registering special* BeanPostProcessors etc in certain ApplicationContext implementations.* @param beanFactory the bean factory used by the application context*/protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {}
	/*** Template method which can be overridden to add context-specific refresh work.* Called on initialization of special beans, before instantiation of singletons.* <p>This implementation is empty.* @throws BeansException in case of errors* @see #refresh()*/protected void onRefresh() throws BeansException {// For subclasses: do nothing by default.}
	/*** Register request/session scopes, environment beans, a {@link ServletContextAwareProcessor}, etc.*/@Overrideprotected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {if (this.servletContext != null) {beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext));beanFactory.ignoreDependencyInterface(ServletContextAware.class);}WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext);}//.../*** Initialize the theme capability.*/@Overrideprotected void onRefresh() {this.themeSource = UiApplicationContextUtils.initThemeSource(this);}

GenericWebApplicationContext对两个钩子写了具体的实现并根据自己的需求在refresh时按部就班的触发,形成了类似"子——父——子"的调用关系,这就是模板方法模式的魅力所在。
怎么样,相信大家看到这里会发现其实源码并没有那么晦涩,一些听到就打怵的词汇也没有那么高深。感兴趣的同学可以根据我的描述自己再追溯一下上述源码深度理解下。


模板方法模式的基本思想其实还是想让算法只存在于一处,这样更方便修改。本质上还是为了实现最大化的代码复用。就像我们开始说的PPT模板一样,父类模板方法实现的某些步骤可以直接被子类拿来使用。
这样既统一了算法,也提供了很大的灵活性。父类模板结构稳定,子类实现花样百出。
不过这种设计模式也存在着短板,就是可能引发类爆炸的问题,每一个不同的实现都需要一个子类,系统会逐渐变得笨重。所以一般模板方法都会加上final,防止子类重写模板方法。


关注我,共同进步,每天进步一点点。——Wayne

这篇关于19.严丝合缝的文明——模板方法模式详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/842626

相关文章

python获取网页表格的多种方法汇总

《python获取网页表格的多种方法汇总》我们在网页上看到很多的表格,如果要获取里面的数据或者转化成其他格式,就需要将表格获取下来并进行整理,在Python中,获取网页表格的方法有多种,下面就跟随小编... 目录1. 使用Pandas的read_html2. 使用BeautifulSoup和pandas3.

Python装饰器之类装饰器详解

《Python装饰器之类装饰器详解》本文将详细介绍Python中类装饰器的概念、使用方法以及应用场景,并通过一个综合详细的例子展示如何使用类装饰器,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录1. 引言2. 装饰器的基本概念2.1. 函数装饰器复习2.2 类装饰器的定义和使用3. 类装饰

Spring 中的循环引用问题解决方法

《Spring中的循环引用问题解决方法》:本文主要介绍Spring中的循环引用问题解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录什么是循环引用?循环依赖三级缓存解决循环依赖二级缓存三级缓存本章来聊聊Spring 中的循环引用问题该如何解决。这里聊

MySQL 中的 JSON 查询案例详解

《MySQL中的JSON查询案例详解》:本文主要介绍MySQL的JSON查询的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录mysql 的 jsON 路径格式基本结构路径组件详解特殊语法元素实际示例简单路径复杂路径简写操作符注意MySQL 的 J

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

Pandas统计每行数据中的空值的方法示例

《Pandas统计每行数据中的空值的方法示例》处理缺失数据(NaN值)是一个非常常见的问题,本文主要介绍了Pandas统计每行数据中的空值的方法示例,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是空值?为什么要统计空值?准备工作创建示例数据统计每行空值数量进一步分析www.chinasem.cn处

Python ZIP文件操作技巧详解

《PythonZIP文件操作技巧详解》在数据处理和系统开发中,ZIP文件操作是开发者必须掌握的核心技能,Python标准库提供的zipfile模块以简洁的API和跨平台特性,成为处理ZIP文件的首选... 目录一、ZIP文件操作基础三板斧1.1 创建压缩包1.2 解压操作1.3 文件遍历与信息获取二、进阶技

Windows 上如果忘记了 MySQL 密码 重置密码的两种方法

《Windows上如果忘记了MySQL密码重置密码的两种方法》:本文主要介绍Windows上如果忘记了MySQL密码重置密码的两种方法,本文通过两种方法结合实例代码给大家介绍的非常详细,感... 目录方法 1:以跳过权限验证模式启动 mysql 并重置密码方法 2:使用 my.ini 文件的临时配置在 Wi

一文详解Java异常处理你都了解哪些知识

《一文详解Java异常处理你都了解哪些知识》:本文主要介绍Java异常处理的相关资料,包括异常的分类、捕获和处理异常的语法、常见的异常类型以及自定义异常的实现,文中通过代码介绍的非常详细,需要的朋... 目录前言一、什么是异常二、异常的分类2.1 受检异常2.2 非受检异常三、异常处理的语法3.1 try-

Java中的@SneakyThrows注解用法详解

《Java中的@SneakyThrows注解用法详解》:本文主要介绍Java中的@SneakyThrows注解用法的相关资料,Lombok的@SneakyThrows注解简化了Java方法中的异常... 目录前言一、@SneakyThrows 简介1.1 什么是 Lombok?二、@SneakyThrows