当前位置: 首页 > news >正文

【设计模式】之模板方法模式

系列文章目录

  • 【设计模式】之策略模式

  • 【设计模式】之责任链模式

文章目录

系列文章目录

前言

一、什么是模板方法模式

定义

 角色 

二、为什么要使用模板方法模式

优点

缺点

三、案例

普通案例

模拟Servlet过程案例

总结


前言

今天给大家介绍23种设计模式中的模板方法模式,这个设计模式在Spring框架中还是挺常见的,手写Spring源码的小伙伴必须要掌握的设计模式之一,文章结构还是经典的三连问,是什么?为什么?怎么用?🌈


一、什么是模板方法模式

定义

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

 角色 

  • 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
    • 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
    • 基本方法:是实现算法各个步骤的方法。基本方法又可以分为三种:
      • 钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。
      • 具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。
      • 抽象方法(Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。
  • 具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。

二、为什么要使用模板方法模式

优点

  • 代码复用:模板方法将公共的代码放在父类中,子类可以通过继承来复用这部分代码,从而避免在子类中重复编写相同的代码。
  • 算法统一:模板方法确保了算法的整体结构在子类中是一致的,只是某些步骤的实现有所不同。这有助于保持算法的统一性和完整性。
  • 灵活性:模板方法允许子类通过实现抽象方法来定制算法的特定步骤,从而提供了很大的灵活性。
  • 扩展性:如果需要增加新的算法步骤,只需要在父类的模板方法中添加新的抽象方法,并让子类实现这些方法即可,无需修改现有的子类代码。

缺点

  • 增加继承层次:使用模板方法设计模式会增加类的继承层次,这可能会导致系统变得复杂,并增加维护成本。
  • 子类数量过多:如果算法中有很多可变步骤,那么可能需要定义很多抽象方法,从而导致子类数量过多。这会增加系统的复杂性,并可能导致“爆炸式”的子类增长。
  • 违反单一职责原则:有时模板方法可能会包含过多的逻辑,导致父类承担了过多的职责。这违反了单一职责原则,使得父类变得难以维护和扩展。

因此,在使用模板方法设计模式时,需要权衡其优缺点,根据具体场景和需求来决定是否使用以及如何使用该模式。

三、案例

普通案例

炒菜步骤:打开油烟机->生火->炒菜->关火->关闭油烟机,我们就以这个步骤为例,不管炒什么菜都会经过 打开油烟机、生火、关火、关闭油烟机这四个步骤,因此,我们可以将这几个过程定义为一个模板方法,至于炒什么样的菜,让子类去决定。

public abstract class Cook {public void open(){System.out.println("打开抽油烟机");}public void openFire(){System.out.println("生火");}//设置为抽象方法,由每个对象重写这个方法(决定做什么菜)public abstract void doCook();public void closeFire(){System.out.println("关火");}public void close(){System.out.println("关闭抽油烟机");}//做饭模板方法public final void cook(){this.open();this.openFire();this.doCook();this.closeFire();this.close();}
}

子类去继承Cook类实现里面的抽象方法去决定做什么菜。

public class CookPotato extends Cook{@Overridepublic void doCook() {System.out.println("酸辣土豆丝");}
}public class CookTomato extends Cook{@Overridepublic void doCook() {System.out.println("西红柿炒鸡蛋");}
}
 @Testvoid test1(){Cook cookPotato = new CookPotato();cookPotato.cook();System.out.println("==========");Cook cookTomato = new CookTomato();cookTomato.cook();}/*
打开抽油烟机
生火
酸辣土豆丝
关火
关闭抽油烟机
==========
打开抽油烟机
生火
西红柿炒鸡蛋
关火
关闭抽油烟机
*/

模拟Servlet过程案例

我们平常使用的Servlet就是一个典型的模板方法的设计模式。

我们平常使用一般是继承HttpServlet这个类,然后去重写类面的一些方法,如:doGet、doPost、doPut等等

public class testServlet extends MyHttpServlet{@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) {System.out.println("执行doGet方法");}@Overridepublic void doPost(HttpServletRequest request, HttpServletResponse response) {System.out.println("执行doPost方法");}
}

HttpServlet会继承GenericServlet这个类,HttpServlet会将ServletRequestServletResponse强转为HttpServletRequestHttpServletResponse,并对方法类型进行判断派发。

public abstract class MyHttpServlet extends MyGenericServlet{@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;switch (request.getMethod()){case "GET":this.doGet(request,response);break;case "POST":this.doPost(request,response);break;}}public abstract void doGet(HttpServletRequest request, HttpServletResponse response);public abstract void doPost(HttpServletRequest request, HttpServletResponse response);
}

GenericServlet实现Servlet这个接口,去实现servlet生命周期方法。

public abstract class MyGenericServlet implements Servlet {@Overridepublic void init(ServletConfig servletConfig) throws ServletException {System.out.println("正在初始化");}@Overridepublic ServletConfig getServletConfig() {return null;}@Overridepublic abstract void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException ;@Overridepublic String getServletInfo() {return null;}@Overridepublic void destroy() {System.out.println("正在销毁");}
}

整个大致过程可以总结为:GenericServlet过滤生命周期方法,将service()方法交给HttpServlet去实现,HttpServlet负责将实现具体的方法,最后就得到了我们常见的doGet等方法,我说的这个过程比较简单,其实Servlet整个过程还是挺复杂的,我就抛砖引玉,给大家熟悉下模板方法这个设计模式。


总结

不管哪一种方法模式,都各有利弊,我们平常在开发的过程中,一定要选择最优的解决方式,不可盲目使用。今天的分享就到这里,我们下期再见。✋

相关文章:

【设计模式】之模板方法模式

系列文章目录 【设计模式】之策略模式 【设计模式】之责任链模式 文章目录 系列文章目录 前言 一、什么是模板方法模式 定义 角色 二、为什么要使用模板方法模式 优点 缺点 三、案例 普通案例 模拟Servlet过程案例 总结 前言 今天给大家介绍23种设计模式中的模板方法模式&a…...

【系统架构师】-选择题(十一)

1、紧耦合多机系统一般通过(共享内存)实现多机间的通信。对称多处理器结构(SMP)属于( 紧耦合)系统。 松耦合多机系统又称间接耦合系统,—般是通过通道或通信线路实现计算机间的互连。 2、采用微内核的OS结构…...

前端开发攻略---介绍HTML中的<dialog>标签,浏览器的原生弹框。

1、演示 2、介绍 <dialog> 标签用于定义对话框&#xff0c;即一个独立的窗口&#xff0c;通常用来显示对话框、提示框、确认框等弹出式内容。在对话框中&#xff0c;可以包含文本、表单元素、按钮等内容&#xff0c;用户可以和这些内容进行交互。 3、兼容性 4、示例代码 …...

让外贸业绩翻倍的销售话术分享

业绩翻三倍的话术&#xff0c;今后无论你遇到挑剔、犹豫、理智的顾客&#xff0c;都能轻松搞定。点赞存起来慢慢看&#xff0c;以免找不到。 与客户有效沟通技巧5的20句金句 业绩翻 3 倍&#xff0c;今后无论你遇到挑剔、犹豫、理智的顾客&#xff0c;都能轻松搞定。点赞存起来…...

观测与预测差值自动变化系统噪声Q的自适应UKF(AUKF_Q)MATLAB编写

简述 基于三维模型的UKF&#xff0c;设计一段时间的输入状态误差较大&#xff0c;此时通过对比预测的状态值与观测值的残差&#xff0c;在相应的情况下自适应扩大系统方差Q&#xff0c;构成自适应无迹卡尔曼滤波&#xff08;AUKF&#xff09;&#xff0c;与传统的UKF相比&…...

虚拟数据中心

创建数据中心和连接宿主机 DRS:收集群集内所有主机和虚拟机的资源使用情况信息&#xff0c;并根据特定的运行状况给出建议或迁移虚拟机HA:如果一台主机出现故障&#xff0c;则该主机上运行的所有虚拟机都将立即在同一群集的其他主机上重新启动EVC:增强型vMotionVirtual SAN:集中…...

解决Blender导出FBX文件到Unity坐标轴错误的问题

发现Blender的模型导入到Unity里面有问题,简单研究了下发现是坐标系不同,Unity使用的是左手坐标系,Blender使用的是右手坐标系 。 下面直接将如何解决 首先忽略Blender的右手坐标系以及Z轴朝上的事&#xff0c;依照unity坐标系情况修改模型物体的旋转&#xff0c;以Blender猴…...

基于微信小程序的校园二手闲置物品交易平台的设计与实现

基于微信小程序的校园二手闲置物品交易平台的设计与实现 “Design and Implementation of a Campus Second-Hand Marketplace Platform based on WeChat Mini Program” 完整下载链接:基于微信小程序的校园二手闲置物品交易平台的设计与实现 文章目录 基于微信小程序的校园二…...

java中多线程的3种实现方法

1.继承Thread类 优点&#xff1a;代码简单&#xff0c;可以直接使用Thread类里面的方法。 缺点&#xff1a;扩张性较差&#xff0c;应为在java中&#xff0c;一个类只能继承一个父类。 2.实现Runnable接口 3.实现Callable接口 2和3的优缺点是一样的 优点&#xff1a;扩展性强&…...

【Docker】docker compose服务编排

docker compose 简介 Dockerfile模板文件可以定义一个单独的应用容器&#xff0c;如果需要定义多个容器就需要服务编排。 docker swarm&#xff08;管理跨节点&#xff09; Dockerfile可以让用户管理一个单独的应用容器&#xff1b;而Compose则允许用户在一个模板&#xff08…...

elementui的el-select+el-tree+el-input实现可搜索的下拉树组件

部分实现代码如下 <template> <div><el-selectv-model"item.TableName"placeholder"请选择":disabled"!item.disabled"visible-change"handleVisible"ref"TableName"><el-input placeholder"请输…...

微信公众号排名 SEO的5个策略

随着微信公众号在社交媒体领域的持续发展和普及&#xff0c;如何提升公众号的搜索排名&#xff0c;成为许多运营者关注的焦点。公众号排名SEO&#xff0c;即针对微信公众号进行搜索引擎优化&#xff0c;旨在提高公众号在搜索结果中的曝光率和点击率。下面&#xff0c;我们将深入…...

python烟花代码

在Python中&#xff0c;可以使用多种方式来模拟烟花效果&#xff0c;其中一种常见的方法是使用turtle图形库来绘制。以下是一个简单的示例&#xff0c;展示了如何使用turtle来创建一个烟花效果的动画&#xff1a; import turtle import random# 设置屏幕和背景 screen turtle…...

Python高级编程

描述 集合&#xff0c;列表生成式&#xff0c;生成器&#xff0c;迭代器&#xff0c;切片 Python 中的集合类型是一种无序、不重复的数据容器&#xff0c;用于存储可哈希&#xff08;hashable&#xff09;的元素。Python 提供了两种内置的集合类型&#xff1a;set 和 frozens…...

leetCode75. 颜色分类

leetCode75. 颜色分类 题目思路 代码 class Solution { public:void sortColors(vector<int>& nums) {for(int i 0, j 0, k nums.size() - 1; i < k;){if(nums[i] 0) swap(nums[i],nums[j]);else if(nums[i] 2) swap(nums[i],nums[k--]);else if(nums[i] …...

选择器、pxcook软件、盒子模型

结构伪类选择器 定义&#xff1a;根据结构的元素关系来查找元素。 <title>Document</title><style>li:first-child{color:aqua ;}li:last-child{color: aqua;}li:nth-child(3){color: aqua;}</style> </head> <body><ul><li>…...

商城系统秒杀功能设计思想

业务特点 1、瞬时并发量大&#xff0c;秒杀时会有大量用户在同一时间进行抢购&#xff0c;瞬时并发访问量突增几倍、甚至几十倍以上 2、库存量少&#xff0c;一般秒杀活动商品量很少&#xff0c;这就导致了只有极少量用户能成功购买到。 3、业务和流程较为常见&#xff0c;一般…...

#初始化列表

1.再谈构造函数 1.1构造函数的组成 构造函数包括函数体赋值与初始化列表初始化。 1.2函数体赋值 class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;} private:int _year;int _month;int _day; };虽然上述构造函数调用之后&…...

Vue-组件中的data

一个组件的data选项必须是一个函数。保证每个组件实例&#xff0c;维护独立的一份数据对象。如下图&#xff1a; 组件一旦封装好了&#xff0c;可以使用多次&#xff0c;比如数字框组件使用了三次&#xff1a; 每次创建新的组件实例&#xff0c;都会重新执行一次data函数&#…...

抖音小店达人佣金应该怎么结算呢?给达人设置多少佣金合适?

大家好&#xff0c;我是电商小V 咱们在运营抖音小店的时候一定会遇到被达人催促结算佣金的情况&#xff0c;咱们给达人合作的时候都会遇到过新手达人&#xff0c;就是给咱们直播带货之后催促咱们赶紧结算商品的佣金&#xff0c; 但是咱们都需要知道一点&#xff0c;那就是小店的…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 &#xff08;1&#xff09;连接查询&#xff08;JOIN&#xff09; 内连接&#xff08;INNER JOIN&#xff09;&#xff1a;返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...