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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/769421.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言字符函数与字符串函数:编织文字的舞会之梦(下)

欢迎来到白刘的领域 Miracle_86.-CSDN博客 系列专栏 C语言知识 先赞后看&#xff0c;已成习惯 创作不易&#xff0c;多多支持&#xff01; 目录 七、strncpy的使用以及模拟实现 八、strncat的使用以及模拟实现 九、strncmp的使用以及模拟实现 十、strstr的使用以及模拟…

Python 使用 PyQt5 设计一个查询IP对话框程序

当前环境&#xff1a;Win10 x64 Python 3.8.10 PyQt5.15.2 PyQt-tools5.15.9.33 1 打开 designer.exe ,新建一个 Dialog without Buttons , 设计窗体。 C:\Python\Python38-32\Lib\site-packages\qt5_applications\Qt\bin\designer.exe 2 使用命令转换为 py C:\Python\Pyth…

在 Windows 中安装配置并启动运行 Jenkins【图文详细教程】

安装 Jenkins 的系统要求&#xff1a; 最少 256MB 可用内存最少 1GB 可用磁盘空间JDK 8 / 11 /17&#xff08;Jenkins 是用 Java 写的&#xff0c;打包成 war 包&#xff09; 查看 JDK 的版本 Java JDK 在 Windows 中安装可以参考&#xff1a;https://www.yuque.com/u27599042/…

AOI检测是如何逐步渗透进半导体领域

欢迎关注GZH《光场视觉》 一直以来AOI检测都是制造业视觉检测系统产业的核心要素。 AOI检测技术应运而生的背景是&#xff1a;电子元件集成度与精细化程度高&#xff0c;检测速度与效率更高、检测零缺陷的发展需求。 在制造业视觉检测系统中下游应用领域中&#xff0c;AOI检测…

vue相关的一些知识总结

一、前言 这里会记录一些Vue的学习和实践路上的一些琐碎知识的总结&#xff0c;很多东西不用深入去了解&#xff0c;或者简单记录即可&#xff0c;深入了解可以去搜别的开发者的总结。 目录 一、前言 二、Vue 相关知识 Vite 和 Vue CLI 单文件组件和多文件组件 prototype …

波奇学Linux:网络接口

127.0.0.1本地回环ip&#xff0c;用于本地测试&#xff0c;不会进行网络通信 TCP是面向连接的&#xff0c;服务器比较被动 需要服套接字监听 listen状态 正常通信默认会进行主机序列和网络序列的转换 TcpServer.cc #pragma once#include<iostream> #include<string…

一分钟学习Markdown语法

title: 一分钟学习Markdown语法 date: 2024/3/24 19:33:29 updated: 2024/3/24 19:33:29 tags: MD语法文本样式列表结构链接插入图片展示练习实践链接问题 欢迎来到Markdown语法的世界&#xff01;Markdown是一种简单而直观的标记语言&#xff0c;让文本排版变得轻松有趣。接下…

详解mysql安装与配置,及Mac中常见的安装问题

目录 1 数据库介绍 什么是数据库 数据库分类 2 MySQL服务器安装 2.1 Windows绿色安装 2.2 Windows中重装MySQL 3 Mac中常见的安装问题 4 客户端连接MySQL服务器 5 SQL分类 1 数据库介绍 什么是数据库 存储数据用文件就可以了&#xff0c;为什么还要弄个数据库? 文件…

ssm001高校专业信息管理系统设计与实现+jsp

高校专业信息管理系统的设计与实现 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对高校专业信息管理混乱&#xff…

【C语言】tcp_sendmsg_locked

一、讲解 tcp_sendmsg_locked 函数是 Linux 内核中实现 TCP 数据发送的一个核心函数。这个函数被调用来将用户空间的数据通过 TCP 发送出去。以下是该函数的基本工作流程的中文解释&#xff1a; 1. 函数初始化和检查&#xff1a; - 它首先检查是否使用了 TCP 零拷贝发送&am…

【Internet结构和ISP,分组延时、丢失和吞吐量】

文章目录 一、Internet结构和ISP1.互联网络结构&#xff1a;网络的网络2.Internet 结构&#xff1a;network of networks 二、分组延时、丢失和吞吐量1.分组丢失和延时是怎样发生的&#xff1f;2.四种分组延时3.分组丢失4.吞吐量 一、Internet结构和ISP 1.互联网络结构&#x…

(1) 易经与命运_学习笔记

个人笔记&#xff0c;斟酌阅读 占卦的原理 三个铜板&#xff0c;正面是3&#xff0c;反面2&#xff0c;三个一起转&#xff0c;得出6,7,8,9 数字象6老阴7少阳8少阴9老阳 生数和成数 生数和成数应该说出自《河图》。其中一二三四五为生数&#xff0c;六七八九十为成数。 生…

一小时学习redis!

redis 基于内存的数据存储系统 三种使用方式 redis优势 安装redis 最后一种方式只能得到5.0的redis版本 比较老&#xff01; 启动redis redis-server.exe 命令 停止ctrlc或关闭 启动客户端 redis-cli redisinsight安装 字符串 redis区分大小写 默认使用字符串存储 二进制…

iOS开发 - 转源码 - __weak问题解决

iOS开发 - 转源码 - __weak问题解决 在使用clang转换OC为C代码时&#xff0c;可能会遇到以下问题 cannot create __weak reference in file using manual reference 原因 __weak弱引用是需要runtime支持的&#xff0c;如果我们还只是使用静态编译&#xff0c;是无法正常转换的…

Redis持久化【RDB,bgsave的写时复制机制】【AOF,aof重写机制】【Redis混合持久化,以及对应改变aof重写规则】【Redis数据备份策略】

Redis持久化 RDB快照&#xff08;snapshot&#xff09;bgsave的写时复制(COW)机制 AOF&#xff08;append-only file&#xff09;AOF重写 Redis 4.0 混合持久化开启持久化后&#xff0c;AOF重写规则发生了变化 Redis数据备份策略&#xff1a; 转自 图灵课堂 RDB快照&#xff0…

如何学习VBA_3.2.19:利用Shell函数运行可执行程序

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的劳动效率&#xff0c;而且可以提高数据处理的准确度。我推出的VBA系列教程共九套和一部VBA汉英手册&#xff0c;现在已经全部完成&#xff0c;希望大家利用、学习。 如果…

【暴刷力扣】11. 盛最多水的容器

11. 盛最多水的容器 题目 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xf…

leetcode216组合总和III

本题思考&#xff1a; 对于输入样例k3,n9 输出里面为什么只有 [[1,2,6],[1,3,5],[2,3,4]]而没有下图所示的重复情况出现呢&#xff1f; 当时代码写错了&#xff0c;思考许久不得解&#xff0c;后面经过仔细对比代码之后发现是我的代码出现了逻辑错误&#xff0c;而正是这一关键…

前端Webpack5高级进阶课程

课程介绍 本套视频教程主要内容包含React/Vue最新版本脚手架分析、基于Webpack5编写自己的loader和plugin等&#xff0c;让你开发时选择更多样&#xff0c;最后&#xff0c;用不到一百行的代码实现Webpack打包。通过本套视频教程的学习&#xff0c;可以帮你彻底打通Webpack的任…

Go——指针和内存逃逸

区别于C/C中的指针&#xff0c;Go语言中的指针不能进行偏移和运算&#xff0c;是安全指针。 要搞明白Go语言中的指针概念需要先知道3个概念&#xff1a;指针地址&#xff0c;指针类型和指针取值。 一. Go语言的指针 Go语言中的函数传参都是值拷贝&#xff0c;当我们想修改某个…