【设计模式】【行为型模式】模板方法模式(Template Method)
👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
📫 欢迎+V: flzjcsg2,我们共同讨论Java深渊的奥秘
🎵 当你的天空突然下了大雨,那是我在为你炸乌云
文章目录
- 一、入门
- 1.1、什么是模板方法模式?
- 1.2、为什么要模板方法模式?
- 1.2.1、代码复用
- 1.2.2、符合开闭原则
- 1.2.3、支持框架设计
- 1.3、怎么实现模板方法模式?
- 二、模板方法模式在源码中运用
- 2.1、Java集合
- 2.2、AQS
- 三、总结
- 参考
一、入门
1.1、什么是模板方法模式?
模板模式(Template Method Pattern)是一种行为设计模式,它定义了一个算法的框架,并允许子类在不改变算法结构的情况下重新定义算法的某些步骤。模板模式通过将算法的通用部分放在父类中,而将可变部分留给子类来实现。
1.2、为什么要模板方法模式?
1.2.1、代码复用
模板模式的核心是将算法的通用部分放在父类中实现,而将可变的部分留给子类去实现。通过这种方式,模板模式实现了代码复用,同时提供了灵活性。
1.2.2、符合开闭原则
符合开闭原则,开闭原则(Open/Closed Principle)要求软件实体(类、模块、函数等)对扩展开放,对修改关闭。模板模式通过将可变的部分抽象出来,允许子类扩展算法的某些步骤,而无需修改父类的代码。 这符合开闭原则,提高了系统的可维护性和可扩展性。
示例:
在 Java 的 HttpServlet 中,service() 方法定义了处理 HTTP 请求的框架,但具体的 doGet() 和 doPost() 方法由子类实现。如果需要支持新的 HTTP 方法,只需添加新的方法,而无需修改 service() 方法。
1.2.3、支持框架设计
模板模式在框架设计中非常有用。框架通常定义了一个通用的流程,但具体的实现由开发者完成。模板模式允许框架提供默认实现,同时允许开发者自定义某些步骤。
模板模式为框架设计提供了一种灵活且可扩展的方式。
示例:
在 Spring MVC 中,AbstractController 定义了处理 HTTP 请求的框架,但具体的请求处理逻辑由开发者实现。
1.3、怎么实现模板方法模式?
定义一个抽象类: 写一个抽象类,里面包含一个模板方法(通常是 final 的),用来定义算法的步骤。在模板方法中,调用一些具体方法(有默认实现)和抽象方法(需要子类实现)。
实现具体类:创建一个子类,继承抽象类。实现抽象类中的抽象方法,提供具体的逻辑。
使用模板模式: 创建具体类的对象,调用模板方法。模板方法会自动按照定义好的步骤执行,同时调用子类实现的逻辑。
【案例】炒菜
炒菜的步骤是固定的,分为倒油、热油、倒菜、倒调料、翻炒等步骤。

AbstractClass类。这个abstract关键字只能在抽象类中,且子类必须实现abstract修饰的方法,就像2楼是盖在1楼上的。final关键字不是必须加的,看实际业务情况,加上后可以避免子类重写该方法。
public abstract class AbstractClass {public final void cookProcess() {//第一步:倒油this.pourOil();//第二步:热油this.heatOil();//第三步:倒蔬菜this.pourVegetable();//第四步:倒调味料this.pourSauce();//第五步:翻炒this.fry();}public void pourOil() {System.out.println("倒油");}//第二步:热油是一样的,所以直接实现public void heatOil() {System.out.println("热油");}//第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)public abstract void pourVegetable();//第四步:倒调味料是不一样public abstract void pourSauce();//第五步:翻炒是一样的,所以直接实现public void fry(){System.out.println("炒啊炒啊炒到熟啊");}
}
ConcreteClass_BaoCai 类
public class ConcreteClass_BaoCai extends AbstractClass {@Overridepublic void pourVegetable() {System.out.println("下锅的蔬菜是包菜");}@Overridepublic void pourSauce() {System.out.println("下锅的酱料是辣椒");}
}
ConcreteClass_CaiXin类
public class ConcreteClass_CaiXin extends AbstractClass {@Overridepublic void pourVegetable() {System.out.println("下锅的蔬菜是菜心");}@Overridepublic void pourSauce() {System.out.println("下锅的酱料是蒜蓉");}
}
Clinet
public class Client {public static void main(String[] args) {//炒手撕包菜ConcreteClass_BaoCai baoCai = new ConcreteClass_BaoCai();baoCai.cookProcess();//炒蒜蓉菜心ConcreteClass_CaiXin caiXin = new ConcreteClass_CaiXin();caiXin.cookProcess();}
}
二、模板方法模式在源码中运用
2.1、Java集合
AbstractList 是抽象类,提供了列表和集合的基本能力。其中的通用addAll方法,但是具体的实现add方法的实现,交给了子类
public boolean addAll(int index, Collection<? extends E> c) {rangeCheckForAdd(index);boolean modified = false;for (E e : c) {add(index++, e); // 子类来实现modified = true;}return modified;
}public void add(int index, E element) {throw new UnsupportedOperationException();
}
ArrayList的实现
public void add(int index, E element) {rangeCheckForAdd(index);ensureCapacityInternal(size + 1); // Increments modCount!!System.arraycopy(elementData, index, elementData, index + 1,size - index);elementData[index] = element;size++;
}
LinkedList的实现
public void add(int index, E element) {checkPositionIndex(index);if (index == size)linkLast(element);elselinkBefore(element, node(index));
}
2.2、AQS
AQS 主要用于实现锁和同步器,它提供了抽象的框架方法,而具体的同步操作实现(如锁的获取、释放)则由子类来实现。这就是模板方法模式的关键特征:定义一个算法的骨架,将一些步骤延迟到子类中。
这里用tryAcquire和tryRelease举例
protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();
}protected boolean tryRelease(int arg) {throw new UnsupportedOperationException();
}
ReentrantLock 中 Sync类实现。ReentrantLock 是 AQS 的一个典型子类实现,它通过内部类 Sync 实现了tryAcquire和tryRelease方法。
protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState(); // 获取当前同步状态if (c == 0) { // 如果状态为0,表示锁未被占用if (!hasQueuedPredecessors() && // 检查是否有线程在等待compareAndSetState(0, acquires)) { // CAS 设置状态setExclusiveOwnerThread(current); // 设置当前线程为独占线程return true;}}else if (current == getExclusiveOwnerThread()) { // 如果是重入int nextc = c + acquires;if (nextc < 0) // 溢出检查throw new Error("Maximum lock count exceeded");setState(nextc); // 更新状态return true;}return false; // 获取锁失败
}protected final boolean tryRelease(int releases) {int c = getState() - releases; // 计算新的状态if (Thread.currentThread() != getExclusiveOwnerThread()) // 检查当前线程是否持有锁throw new IllegalMonitorStateException();boolean free = false;if (c == 0) { // 如果状态为0,表示锁完全释放free = true;setExclusiveOwnerThread(null); // 清除独占线程}setState(c); // 更新状态return free;
}
AQS 在acquire和release方法中会调用子类实现的tryAcquire和tryRelease。
public final void acquire(int arg) {if (!tryAcquire(arg) && // 尝试获取锁acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 如果失败,加入队列并阻塞selfInterrupt();
}public final boolean release(int arg) {if (tryRelease(arg)) { // 尝试释放锁Node h = head;if (h != null && h.waitStatus != 0)unparkSuccessor(h); // 唤醒队列中的下一个线程return true;}return false;
}
三、总结
模板方法的结构:
● 抽象类(Abstract Class):定义一个算法的框架,包含一个模板方法和一些基本操作(可以是抽象的或已经实现的)。
● 模板方法(Template Method):在抽象类中定义的具体方法,它通过调用其他步骤(基本操作)来实现算法的步骤,子类可以通过覆盖某些步骤来调整具体的行为。
● 具体子类(Concrete Class):继承自抽象类,并实现或重写抽象类中的一些方法。
优点:
- 代码复用:模板方法通过将公共的算法框架定义在父类中,减少了子类中重复代码的编写。
- 扩展性:子类可以根据需要修改或扩展特定的步骤,而无需改变算法的整体结构。
- 控制算法流程:通过模板方法,父类可以控制整个算法的执行流程,确保子类在特定的顺序中执行步骤。
- 提高一致性:算法的骨架是固定的,这使得不同的子类可以按照相同的步骤进行操作,增强了程序的可维护性。
缺点:
- 增加类的数量:每次修改和扩展都可能需要增加新的子类,这可能会使系统类的数量增加,导致复杂度提高。
- 不灵活的继承结构:模板方法的设计依赖于继承,如果需要灵活变化或组合不同的步骤,可能会导致类的设计过于僵化。
- 难以适应复杂的变化:当算法变得非常复杂或频繁变化时,可能需要频繁修改父类的模板方法,这可能不太符合开闭原则。
适用场景:
- 有多个步骤的算法:当一个操作包含多个步骤,而这些步骤可以通过不同的子类进行调整时,模板方法模式非常适用。例如,游戏中不同角色的攻击动作,或者不同文档生成过程的步骤等。
- 有部分固定操作和部分可变操作的场景:当某些步骤是固定的,而其他步骤可能会变化时,模板方法模式可以帮助实现固定部分的复用,并允许子类根据需要重写可变部分。
- 不希望改变算法结构时:如果你需要确保子类按照某种固定的顺序执行步骤,但又希望在某些步骤上有所不同,模板方法可以很好地适用。
参考
重学 Java 设计模式:实战模板模式「模拟爬虫各类电商商品,生成营销推广海报场景」
黑马程序员Java设计模式详解, 23种Java设计模式(图解+框架源码分析+实战)_哔哩哔哩_bilibili
相关文章:
【设计模式】【行为型模式】模板方法模式(Template Method)
👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD 🔥 2025本人正在沉淀中… 博客更新速度 📫 欢迎V: flzjcsg2,我们共同讨论Java深渊的奥秘 …...
w200基于spring boot的个人博客系统的设计与实现
🙊作者简介:多年一线开发工作经验,原创团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹赠送计算机毕业设计600个选题excel文…...
React 生命周期函数详解
React 组件在其生命周期中有多个阶段,每个阶段都有特定的生命周期函数(Lifecycle Methods)。这些函数允许你在组件的不同阶段执行特定的操作。以下是 React 组件生命周期的主要阶段及其对应的生命周期函数,并结合了 React 16.3 的…...
docker grafana安装
mkdir /root/grafana-storage chmod 777 -R /root/grafana-storage docker run -d -p 3000:3000 --namedocker-apisix-grafana-1 --network docker-apisix_apisix -v /root/grafana-storage:/var/lib/grafana grafana/grafana:9.1.0 浏览器访问: http://192.…...
H5+CSS+JS制作好看的轮播图
先来看效果 点击下方按钮可以做到平滑切换轮播,轮播图片可以根据自定义随心变化。 先来看一下页面代码结构 <div class"container"><div class"lunbo-wrap"><div id"slide"></div><div class"butto…...
aio-pika 快速上手(Python 异步 RabbitMQ 客户端)
目录 简介官方文档如何使用 简介 aio-pika 是一个 Python 异步 RabbitMQ 客户端。5.0.0 以前 aio-pika 基于 pika 进行封装,5.0.0 及以后使用 aiormq 进行封装。 https://github.com/mosquito/aio-pikahttps://pypi.org/project/aio-pika/ pip install aio-pika官…...
表单与交互:HTML表单标签全面解析
目录 前言 一.HTML表单的基本结构 基本结构 示例 二.常用表单控件 文本输入框 选择控件 文件上传 按钮 综合案例 三.标签的作用 四.注意事项 前言 HTML(超文本标记语言)是构建网页的基础,其中表单(<form>&…...
非递减子序列(力扣491)
这道题的难点依旧是去重,但是与之前做过的子集类问题的区别就是,这里是求子序列,意味着我们不能先给数组中的元素排序。因为子序列中的元素的相对位置跟原数组中的相对位置是一样的,如果我们改变数组中元素的顺序,子序…...
Python基础-元组tuple的学习
在 Python 中,元组(tuple)是一种不可变的序列类型,允许存储不同类型的元素。元组非常类似于列表(list),但与列表不同的是,元组一旦创建,就不能修改其内容。 1 元组的创建…...
Vue与Konva:解锁Canvas绘图的无限可能
前言 在现代Web开发中,动态、交互式的图形界面已成为提升用户体验的关键要素。Vue.js,作为一款轻量级且高效的前端框架,凭借其响应式数据绑定和组件化开发模式,赢得了众多开发者的青睐。而当Vue.js邂逅Konva.js,两者结…...
如何修改DNS解析?
DNS(域名系统)就像互联网的“电话簿”,负责将我们输入的网址转换为计算机能够理解的IP地址。如果DNS解析出现问题,访问网站就会受到影响。那我们该如何修改DNS解析呢?接下来,我们就来介绍一下这个话题。 为什么要修改DNS解析? 使用默认的…...
go语言文件和目录
打开和关闭文件 os.Open()函数能够打开一个文件,返回一个*File 和一个 err。操作完成文件对象以后一定要记得关闭文件。 package mainimport ("fmt""os" )func main() {// 只读方式打开当前目录下的 main.go 文件file, err : os.Open(".…...
Solidity09 Solidity构造函数和修饰器
文章目录 一、构造函数二、修饰器三、OpenZeppelin的Ownable标准实现四、Remix 演示示例五、代码示例 这一讲,我们将用合约权限控制( Ownable)的例子介绍 Solidity语言中构造函数( constructor)和独有的修饰器&…...
ES6 Map 数据结构是用总结
1. Map 基本概念 Map 是 ES6 提供的新的数据结构,它类似于对象,但是"键"的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也可以跟踪键值对的原始插入顺序。 1.1 基本用法 // 创建一个空Map…...
使用wpa_supplicant和wpa_cli 扫描wifi热点及配网
一:简要说明 交叉编译wpa_supplicant工具后会有wpa_supplicant和wpa_cli两个程序生产,如果知道需要连接的wifi热点及密码的话不需要遍历及查询所有wifi热点的名字及信号强度等信息的话,使用wpa_supplicant即可,否则还需要使用wpa_…...
json转excel,在excel内导入json, json-to-excel插件
简介 JSON 转 Excel 是一款 Microsoft Excel 插件,可将 JSON 数据转换为 Excel 表格。 要求 此插件适用于以下环境:Excel 2013 Service Pack 1 或更高版本、Excel 2016 for Mac、Excel 2016 或更高版本、Excel Online。 快速开始 本快速开始指南适用…...
大模型应用与实战:专栏概要与内容目录
文章目录 大模型应用与实战📚 核心内容模块一、大模型推理与部署1.1 推理框架应用实践1.2 框架源码深度解析1.3 高并发部署优化1.4 国产化平台适配 二、Agent框架专题2.1 Langchain系列2.2 Qwen-Agent系列2.3 Dify应用实践2.4 框架对比与迁移 三、微调技术研究3.1 微…...
ZooKeeper 的典型应用场景:从概念到实践
引言 在分布式系统的生态中,ZooKeeper 作为一个协调服务框架,扮演着至关重要的角色。它的设计目的是提供一个简单高效的解决方案来处理分布式系统中常见的协调问题。本文将详细探讨 ZooKeeper 的典型应用场景,包括但不限于配置管理、命名服务…...
Arbess基础教程-创建流水线
Arbess(谐音阿尔卑斯) 是一款开源免费的 CI/CD 工具,本文将介绍如何使用 Arbess 配置你的第一条流水线,以快速入门上手。 1. 创建流水线 根据不同需求来创建不同的流水线。 1.1 配置基本信息 配置流水线的基本信息,如分组,环境&…...
科普书《从一到无穷大》的科普知识推翻百年集论
科普书《从一到无穷大》的科普知识推翻百年集论 黄小宁 “我们给两组无穷大数列中的各个数一一配对,如果最后这两组都一个不剩,这两组无穷大就是相等的;如果有一组还有些数没有配出去,这一组就比另一组大些,或者说强些…...
【键盘识别】实例分割
第一步 键盘检测 方案一 canny边缘检测 canny边缘检测检测结果不稳定,容易因为复杂背景或光线变换检测出其他目标。 如图是用canny边缘检测方法标出的检测出的边缘的四个红点。 参考的是这篇文章OpenCV实战之三 | 基于OpenCV实现图像校正_opencv 图像校正-CSDN博客 方案二…...
25/2/7 <机器人基础>雅可比矩阵计算 雅可比伪逆
雅可比矩阵计算 雅可比矩阵的定义 假设我们有一个简单的两个关节的平面机器人臂,其末端执行器的位置可以表示为: 其中: L1 和 L2 是机器人臂的长度。θ1 和 θ2是关节的角度。 计算雅可比矩阵 雅可比矩阵 JJ 的定义是将关节速度与末…...
apisix的real-ip插件使用说明
k8s集群入口一般都需要过负载均衡,然后再到apisix。 这时候如果后台业务需要获取客户端ip,可能拿到的是lb或者网关的内网ip。 这里一般要获取真实ip需要做几个处理。 1. 负载均衡上,一般支持配置获取真实ip参数,需要配置上。然…...
位图的深入解析:从数据结构到图像处理与C++实现
在学习优选算法课程的时候,博主学习位运算了解到位运算的这个概念,之前没有接触过,就查找了相关的资料,丰富一下自身,当作课外知识来了解一下。 位图(Bitmap)是一种用于表示图像的数据结构&…...
Python实现GO鹅优化算法优化支持向量机SVM分类模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档视频讲解),如需数据代码文档视频讲解可以直接到文章最后关注获取。 1.项目背景 随着信息技术的迅猛发展,数据量呈爆炸式增长,如何从海量的数据中提取有价值…...
Deno vs Node.js:性能对比深度解析
1. 引言 Deno 和 Node.js 都是基于 V8 引擎的 JavaScript 运行时环境,然而它们在架构、模块管理、安全性和性能方面存在显著差异。Deno 由 Node.js 的原始作者 Ryan Dahl 开发,旨在解决 Node.js 设计上的一些问题,比如包管理、安全模型和 Ty…...
我用AI做数据分析之数据清洗
我用AI做数据分析之数据清洗 AI与数据分析的融合效果怎样? 这里描述自己在使用AI进行数据分析(数据清洗)过程中的几个小故事: 1. 变量名的翻译 有一个项目是某医生自己收集的数据,变量名使用的是中文,分…...
备战蓝桥杯:双指针(滑动窗口)算法之逛花展
P1638 逛画展 - 洛谷 | 计算机科学教育新生态 这道题我们只要用一个kind和一个mp[N]的数组就能解决了 我们的解法1就是暴力枚举,先固定2,从2开始找连续的满足所有种类的最短的子数组,然后固定5,3,1,3&…...
【sqlite】python操作sqlite3(含测试)
个人小项目或者小团队,sqllite很适用,数据库封装操作如下 #!/usr/bin/env python # -*- coding: utf-8 -*- # Time : 2025-02-08 13:57 # Author : duxiaowei # File : connect_sqllite.py # Software: PyCharm """ sqllite操作, …...
mybatis 是否支持延迟加载?延迟加载的原理是什么?
1. MyBatis 是否支持延迟加载? 是的,MyBatis 支持延迟加载。延迟加载的主要功能是推迟数据加载的时机,直到真正需要时再去加载。这种方式能提高性能,尤其是在处理关系型数据时,可以避免不必要的数据库查询。 具体来说…...
