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

深入理解设计模式-行为型之模板(和回调区别联系)

概述

模板设计模式(Template Design Pattern)是一种行为型设计模式,它定义了一个算法的骨架,将算法的一些步骤延迟到子类中实现。模板设计模式允许子类在不改变算法结构的情况下重新定义算法的某些步骤。

模板设计模式的核心思想是:将一个算法的主要结构定义在一个模板方法中,而将具体(某些)步骤的实现交给子类去完成。

// 模板类 抽象类:Beverage-->饮料
abstract class Beverage {// 模板方法,定义算法的骨架public final void prepareRecipe() {boilWater();brew();pourInCup();addCondiments();}// 具体步骤,由子类实现abstract void brew();abstract void addCondiments();// 公共方法void boilWater() {System.out.println("Boiling water");}void pourInCup() {System.out.println("Pouring into cup");}
}// 具体子类
class Coffee extends Beverage {void brew() {System.out.println("Dripping coffee through filter");}void addCondiments() {System.out.println("Adding sugar and milk");}
}class Tea extends Beverage {void brew() {System.out.println("Steeping the tea");}void addCondiments() {System.out.println("Adding lemon");}
}// 客户端代码
public class TemplateExample {public static void main(String[] args) {Beverage coffee = new Coffee();Beverage tea = new Tea();System.out.println("Making coffee:");coffee.prepareRecipe();System.out.println("\nMaking tea:");tea.prepareRecipe();}
}

在这个示例中,Beverage 是模板类,定义了模板方法 prepareRecipe(),其中包含了煮水、冲泡、倒入杯子和加调料的步骤。brew() 和 addCondiments() 是具体步骤(冲泡和加调料),由子类实现。Coffee 和 Tea 是具体子类,分别实现了不同的冲泡和调料步骤。

通过模板设计模式,模板类 Beverage 提供了一个通用的算法骨架,而具体步骤的实现交给子类。这样可以确保算法的结构一致,同时允许不同子类根据自身特点进行实现。

使用场景、源码应用

模板设计模式在许多场景下都可以应用,特别是在需要定义一组具有共同流程的操作时,但每个操作可能有不同的实现细节。以下是一些常见的应用场景:

  • 框架和库:许多框架和库使用模板设计模式来定义通用的操作流程,然后允许用户通过子类来实现特定的操作细节。比如,数据库操作框架可以定义一个通用的操作流程,然后用户可以通过继承来实现特定数据库的连接和操作。

  • 算法实现:在某些算法中,有一些步骤是通用的,但有些步骤可能因情况而异。模板设计模式允许你将通用的步骤放在模板方法中,然后由子类来实现不同的步骤。

  • 工作流程:在工作流程管理中,可以使用模板设计模式来定义通用的工作流程,然后让不同的流程实例来实现具体的任务。

  • 生命周期管理:在许多应用中,有一些生命周期的操作是通用的,例如初始化、清理资源等。模板设计模式可以用于定义这些通用的生命周期操作。

在源码中,模板设计模式也有许多应用。以下是一些示例:

  • Java Servlet:在 Java Servlet 中,HttpServlet 就是一个使用模板设计模式的例子。HttpServlet 定义了 service() 方法作为模板方法,然后具体的 HTTP 请求处理由不同的子类来实现。

  • JUnit 测试框架:在 JUnit 中,测试用例的执行过程也是一个典型的模板设计模式。JUnit 提供了测试用例的生命周期方法,例如 setUp() 和 tearDown(),然后用户可以在子类中实现这些方法来执行测试。

  • Spring Framework:在 Spring 中,JdbcTemplate 类用于执行数据库操作,它将数据库操作的通用流程定义在模板方法中,而具体的 SQL 执行由用户提供的回调函数实现。

这些只是一些示例,模板设计模式在许多框架和库中都有广泛的应用,它提供了一种结构化的方式来定义通用的操作流程,并允许具体实现在子类中进行定制。

Java Servlet

对于 Java Web 项目开发来说,常用的开发框架是 SpringMVC。利用它,我们只需要关注业务代码的编写,底层的原理几乎不会涉及。但是,如果我们抛开这些高级框架来开发 Web 项目,必然会用到 Servlet。实际上,使用比较底层的 Servlet 来开发 Web 项目也不难。我们只需要定义一个继承 HttpServlet 的类,并且重写其中的 doGet() 或 doPost() 方法,来分别处理 get 和 post 请求。具体的代码示例如下所示:

public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("Hello World.");}
}

除此之外,我们还需要在配置文件 web.xml 中做如下配置。Tomcat、Jetty 等 Servlet 容器在启动的时候,会自动加载这个配置文件中的 URL 和 Servlet 之间的映射关系。

<servlet><servlet-name>HelloServlet</servlet-name><servlet-class>com.xzg.cd.HelloServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>HelloServlet</servlet-name><url-pattern>/hello</url-pattern>
</servlet-mapping>

当我们在浏览器中输入网址(比如,http://127.0.0.1:8080/hello )的时候,Servlet 容器会接收到相应的请求,并且根据 URL 和 Servlet 之间的映射关系,找到相应的 Servlet(HelloServlet),然后执行它的 service() 方法service() 方法定义在父类 HttpServlet 中,它会调用 doGet() 或 doPost() 方法,然后输出数据(“Hello world”)到网页。我们现在来看,HttpServlet 的 service() 函数长什么样子。

public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException{HttpServletRequest  request;HttpServletResponse response;if (!(req instanceof HttpServletRequest &&res instanceof HttpServletResponse)) {throw new ServletException("non-HTTP request or response");}request = (HttpServletRequest) req;response = (HttpServletResponse) res;service(request, response);
}protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{String method = req.getMethod();if (method.equals(METHOD_GET)) {long lastModified = getLastModified(req);if (lastModified == -1) {// servlet doesn't support if-modified-since, no reason// to go through further expensive logicdoGet(req, resp);} else {long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);if (ifModifiedSince < lastModified) {// If the servlet mod time is later, call doGet()// Round down to the nearest second for a proper compare// A ifModifiedSince of -1 will always be lessmaybeSetLastModified(resp, lastModified);// 子类实现的扩展点doGet(req, resp);} else {resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);}}} else if (method.equals(METHOD_HEAD)) {long lastModified = getLastModified(req);maybeSetLastModified(resp, lastModified);doHead(req, resp);} else if (method.equals(METHOD_POST)) {// 子类实现的扩展点doPost(req, resp);} else if (method.equals(METHOD_PUT)) {// 子类实现的扩展点doPut(req, resp);} else if (method.equals(METHOD_DELETE)) {// 子类实现的扩展点doDelete(req, resp);} else if (method.equals(METHOD_OPTIONS)) {// 子类实现的扩展点doOptions(req,resp);} else if (method.equals(METHOD_TRACE)) {// 子类实现的扩展点doTrace(req,resp);} else {String errMsg = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[1];errArgs[0] = method;errMsg = MessageFormat.format(errMsg, errArgs);resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);}

从上面的代码中我们可以看出,HttpServlet 的 service() 方法就是一个模板方法,它实现了整个 HTTP 请求的执行流程,**doGet()、doPost() 是模板中可以由子类来定制的部分。**实际上,这就相当于 Servlet 框架提供了一个扩展点(doGet()、doPost() 方法),让框架用户在不用修改 Servlet 框架源码的情况下,将业务代码通过扩展点镶嵌到框架中执行。

模板模式与Callback回调函数有何区别和联系?

联系:

  • 共同点:两者都涉及将一些逻辑从调用代码中抽离出来,使代码更具模块化和可维护性。
  • 抽象步骤:在模板模式中,一个通用的算法框架定义了一系列的抽象步骤,子类可以通过实现这些步骤来完成特定的行为。在回调函数中,一个函数可以接受一个回调函数作为参数,使调用者能够在适当的时候执行这个回调函数,完成特定的操作。

区别:

  • 角色和目的:

    • 模板模式:主要目的是在超类中定义算法的骨架,而将一些具体步骤的实现推迟到子类中。它更关注整个流程的结构和控制。
    • 回调函数:主要目的是允许调用者在某个代码块执行时插入自己的代码逻辑。它更关注于将执行权交给外部代码,以便根据需要执行回调逻辑。
  • 控制权:

    • 模板模式:控制权由超类控制,子类只实现具体的步骤,流程由模板方法决定。
    • 回调函数:控制权在调用者手中,调用者通过提供回调函数来决定在何时执行回调逻辑。
  • 调用关系:

    • 模板模式:子类通过继承超类来实现抽象步骤,超类负责调用子类的方法。
    • 回调函数:调用者将回调函数作为参数传递给被调用者,被调用者在适当的时候调用回调函数。

举例:

一个具体的区别和联系示例可以是在GUI编程中,比如在按钮被点击时要执行的操作。使用模板模式,你可以定义一个通用的按钮点击流程,包括按钮的渲染、点击事件的处理等。使用回调函数,你可以将点击事件处理的逻辑作为一个回调函数传递给按钮组件,以便在按钮被点击时执行。
Java中的 java.util.concurrent 包中的一些类使用了回调来实现多线程编程。例如,Executor 接口中的 execute(Runnable command) 方法就接受一个 Runnable 对象作为回调,用于在线程池中执行任务。

总之,模板模式和回调函数在不同的场景下有不同的应用,但都关注于提高代码的模块化和可重用性,同时也都涉及到将一些代码逻辑从调用者中分离出来。

相关文章:

深入理解设计模式-行为型之模板(和回调区别联系)

概述 模板设计模式&#xff08;Template Design Pattern&#xff09;是一种行为型设计模式&#xff0c;它定义了一个算法的骨架&#xff0c;将算法的一些步骤延迟到子类中实现。模板设计模式允许子类在不改变算法结构的情况下重新定义算法的某些步骤。 模板设计模式的核心思想…...

LabVIEW控制通用工作台

LabVIEW控制通用工作台 用于教育目的的计算机化实验室显着增长&#xff0c;特别是用于运动控制的实验室。它们代表了各种工业应用中不断扩大的领域&#xff0c;并成为以安全的方式使用通常昂贵或独特的实验室设备进行实时实验的宝贵工具。NI LabVIEW等软件应用程序的开发和不断…...

什么是事务,并发带来的事务问题以及事务隔离级别(图文详解)

一、什么是事务&#xff1f; 简单说就是逻辑上的一组操作&#xff0c;要么都执行&#xff0c;要么都不执行。 举个例子&#xff0c;假如小明要给小红转账100元&#xff0c;这个转账会涉及到两个关键操作&#xff1a;①将小明的余额减少100元。 ②将小红的余额增加100元 。但…...

【MySQL】MySQL数据库的delete from table和truncate table之间的区别

DELETE FROM table 和 TRUNCATE TABLE 是两种不同的数据库操作&#xff0c;用于从MySQL数据库的表中删除数据。它们有以下区别&#xff1a; 操作方式&#xff1a;DELETE FROM table 是一种逐行删除的操作&#xff0c;它会逐个删除表中的每一行数据&#xff0c;并且可以带有条件…...

强制Edge或Chrome使用独立显卡【WIN10】

现代浏览器通常将图形密集型任务卸载到 GPU&#xff0c;以改善你的网页浏览体验&#xff0c;从而释放 CPU 资源用于其他任务。 如果你的系统有多个 GPU&#xff0c;Windows 10 可以自动决定最适合 Microsoft Edge 自动使用的 GPU&#xff0c;但这并不一定意味着最强大的 GPU。 …...

easyx图形库基础:3实现弹球小游戏

实现弹球小游戏 一.实现弹球小游戏:1.初始化布&#xff1a;2.初始化一个球的信息&#xff1a;3.球的移动和碰撞反弹4.底边挡板的绘制和移动碰撞重置数据。 二.整体代码&#xff1a; 一.实现弹球小游戏: 1.初始化布&#xff1a; int main() {initgraph(800, 600);setorigin(40…...

vue基础知识三:v-show和v-if有什么区别?使用场景分别是什么?

一、v-show与v-if的共同点 我们都知道在 vue 中 v-show 与 v-if 的作用效果是相同的(不含v-else)&#xff0c;都能控制元素在页面是否显示 在用法上也是相同的 <Model v-show"isShow" /> <Model v-if"isShow" />当表达式为true的时候&#…...

Python Opencv实践 - 图像旋转

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR)#图像旋转 #Opencv中的旋转&#xff0c;首先通过cv.getRotationMatrix2D获得旋转矩阵 #cv.getRotationMatrix2D(center,ang…...

第五章 Opencv图像处理框架实战 5-10 文档扫描OCR识别

一、整体流程演示 上一篇我们进行了银行卡数字识别,这次我们利用opnecv等基础图像处理方法实现文档扫描OCR识别,该项目可以对任何一个文档,识别扫描出该文档上所有的文字信息。 为了方便后续程序运行,大家可以在Run->Edit Configuration中配置相关参数,选择相应编译器…...

CentOS 7 源码制作openssh 9.4p1 rpm包 —— 筑梦之路

参考之前的博客&#xff1a; centos 7 制作openssh8.7/8.8/8.9/9.0/9.1/9.2/9.3 p1 rpm包升级——筑梦之路_openssh rpm包_筑梦之路的博客-CSDN博客 需要说明的是9.4版本必须要openssl 1.1.1&#xff0c;低于此版本无法完成编译。这也是单独写这篇文章的必要性。 参考这篇编…...

OpenCV图像处理——轮廓检测

目录 图像的轮廓查找轮廓绘制轮廓 轮廓的特征轮廓面积轮廓周长轮廓近似凸包边界矩形最小外接圆椭圆拟合直线拟合 图像的矩特征矩的概念图像中的矩特征 图像的轮廓 查找轮廓 binary,contours,hierarchycv.findContours(img,mode,method)绘制轮廓 cv.drawContours(img,coutours…...

【论文阅读】基于深度学习的时序预测——Non-stationary Transformers

系列文章链接 论文一&#xff1a;2020 Informer&#xff1a;长时序数据预测 论文二&#xff1a;2021 Autoformer&#xff1a;长序列数据预测 论文三&#xff1a;2022 FEDformer&#xff1a;长序列数据预测 论文四&#xff1a;2022 Non-Stationary Transformers&#xff1a;非平…...

开发者如何使用讯飞星火认知大模型API?

目录 1、申请星火API接口 2、使用星火API接口 3、测试编译效果 之前我们使用网页文本输入的方式体验了讯飞星火认知大模型的功能&#xff08;是什么让科大讯飞1个月股价翻倍&#xff1f;&#xff09;&#xff0c;本篇博文将从开发者角度来看看如何使用讯飞星火认知大模型API…...

linux 系统中vi 编辑器和库的制作和使用

目录 1 vim 1.1 vim简单介绍 1.2 vim的三种模式 1.3 vim基本操作 1.3.1命令模式下的操作 1.3.2 切换到文本输入模式 1.3.3 末行模式下的操作 2 gcc编译器 2.1 gcc的工作流程 2.2 gcc常用参数 3 静态库和共享&#xff08;动态&#xff09;库 3.1库的介绍 3.2静态…...

麒麟arm架构 编译安装qt5.14.2

1、先在官网下载qt源码&#xff1a; https://download.qt.io/archive/qt/5.14/5.14.2/single/[qt源码下载地址] 2、解压编译 使用tar -xvf qt-everywhere-src-5.14.2.tar.xz 解压压缩包 cd qt-everywhere-src-5.14.2 执行 ./configure --prefix/usr/local/qt.5.14.2 make -…...

【springmvc系】利用RequestBodyAdviceAdapter做接口鉴权

需求 有个简单的需求&#xff0c;对于第三方接口我们需要做个简单的鉴权机制&#xff0c;这边使用的是非对称性加密的机制。我们提供三方公钥&#xff0c;他们通过公钥对接口json报文使用加密后的报文请求&#xff0c;我们通过对接收过来的请求某一个加密报文字段来进行RSA解密…...

ROS学习笔记(三)---好用的终端Terminator

ROS学习笔记文章目录 01. ROS学习笔记(一)—Linux安装VScode 02. ROS学习笔记(二)—使用 VScode 开发 ROS 的Python程序&#xff08;简例&#xff09; 一、Terminator是什么&#xff1f; 在前面的学习中&#xff0c;为了运行hello.py我是在vscode频繁的点击运行窗口的“”号…...

NFT Insider#102:The Sandbox重新上线LAND桥接服务,YGG加入Base生态

引言&#xff1a;NFT Insider由NFT收藏组织WHALE Members(https://twitter.com/WHALEMembers)、BeepCrypto&#xff08;https://twitter.com/beep_crypto&#xff09;联合出品&#xff0c;浓缩每周NFT新闻&#xff0c;为大家带来关于NFT最全面、最新鲜、最有价值的讯息。每期周…...

Webpack 的 sass-loader 在生产模式下最小化 CSS 问题

学习webpack时候我发现一个问题&#xff1a; 将mode 改为production模式后&#xff0c;生成的css会被压缩了&#xff0c;但是我并没有引入CssMinimizerPlugin插件&#xff0c;然后我试着将optimization.minimize 设置为false&#xff0c;测试是否为webpack自带的压缩&#xff0…...

pytest自动化测试框架tep环境变量、fixtures、用例三者之间的关系

tep是一款测试工具&#xff0c;在pytest测试框架基础上集成了第三方包&#xff0c;提供项目脚手架&#xff0c;帮助以写Python代码方式&#xff0c;快速实现自动化项目落地。 在tep项目中&#xff0c;自动化测试用例都是放到tests目录下的&#xff0c;每个.py文件相互独立&…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

k8s从入门到放弃之HPA控制器

k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率&#xff08;或其他自定义指标&#xff09;来调整这些对象的规模&#xff0c;从而帮助应用程序在负…...

Android写一个捕获全局异常的工具类

项目开发和实际运行过程中难免会遇到异常发生&#xff0c;系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler&#xff0c;它是Thread的子类&#xff08;就是package java.lang;里线程的Thread&#xff09;。本文将利用它将设备信息、报错信息以及错误的发生时间都…...

Linux 下 DMA 内存映射浅析

序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存&#xff0c;但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程&#xff0c;可以参考这篇文章&#xff0c;我觉得写的非常…...