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

设计模式 - 行为模式_Template Method Pattern模板方法模式在数据处理中的应用

文章目录

  • 概述
  • 1. 核心思想
  • 2. 结构
  • 3. 示例代码
  • 4. 优点
  • 5. 缺点
  • 6. 适用场景
  • 7. 案例:模板方法模式在数据处理中的应用
    • 案例背景
    • UML
    • 搭建抽象基类 - 数据处理的 “总指挥”
    • 子类定制 - 适配不同供应商
      • 供应商 A 的数据处理器
      • 供应商 B 的数据处理器
    • 在业务代码中整合运用
  • 8. 总结

在这里插入图片描述


概述

模板方法模式(Template Method Pattern)是一种行为设计模式,其核心思想是:定义一个操作的算法骨架,将一些步骤的实现延迟到子类中。在软件设计中,模板方法模式通常用于处理一系列相似的操作,这些操作可以被抽象为固定的流程,而流程中的某些步骤则由子类来具体实现。


1. 核心思想

模板方法模式的核心思想可以总结为以下几点:

  • 固定流程:定义一个固定的算法流程,确保所有子类都遵循相同的步骤。
  • 可变步骤:将某些步骤的具体实现延迟到子类中,允许子类根据需求定制。
  • 代码复用:通过将通用逻辑放在父类中,减少代码重复。

2. 结构

模板方法模式通常由以下几个部分组成:

  1. 抽象类(Abstract Class):定义算法的骨架,并包含一些抽象方法供子类实现。
  2. 具体子类(Concrete Class):实现抽象类中的抽象方法,完成具体的业务逻辑。
  3. 模板方法(Template Method):在抽象类中定义的一个方法,它调用了算法中的各个步骤。

3. 示例代码

一个简单的模板方法模式示例,展示如何定义一个算法骨架并在子类中实现具体步骤。

// 抽象类
abstract class AbstractClass {// 模板方法,定义了算法的骨架public final void templateMethod() {step1();step2();step3();}// 具体方法,子类可以直接使用private void step1() {System.out.println("执行步骤1");}// 抽象方法,子类需要实现protected abstract void step2();// 钩子方法,子类可以选择性重写protected void step3() {System.out.println("执行步骤3");}
}// 具体子类A
class ConcreteClassA extends AbstractClass {@Overrideprotected void step2() {System.out.println("ConcreteClassA 执行步骤2");}
}// 具体子类B
class ConcreteClassB extends AbstractClass {@Overrideprotected void step2() {System.out.println("ConcreteClassB 执行步骤2");}@Overrideprotected void step3() {System.out.println("ConcreteClassB 执行步骤3");}
}// 客户端代码
public class TemplateMethodPatternDemo {public static void main(String[] args) {AbstractClass classA = new ConcreteClassA();classA.templateMethod();AbstractClass classB = new ConcreteClassB();classB.templateMethod();}
}

为了防止恶意修改,模板方法通常使用final关键字修饰,避免被子类重写。


4. 优点

  • 代码复用:通过将通用逻辑放在父类中,减少了代码重复。
  • 扩展性:子类可以通过实现抽象方法来扩展算法的某些步骤,而无需修改算法的整体结构。
  • 灵活性:钩子方法(Hook Method)允许子类选择性地覆盖某些步骤,增加了灵活性。

5. 缺点

  • 复杂性:如果算法的步骤过多,可能会导致类的层次结构变得复杂。
  • 限制性:模板方法模式要求算法的步骤是固定的,这可能会限制某些场景下的灵活性。

6. 适用场景

  • 固定流程:当某个算法的流程是固定的,但某些步骤的具体实现可能不同时,可以使用模板方法模式。
  • 代码复用:当多个子类有共同的逻辑时,可以将这些逻辑提取到父类中,减少代码重复。
  • 框架设计:在框架设计中,模板方法模式常用于定义框架的核心流程,而将具体实现留给用户自定义。

7. 案例:模板方法模式在数据处理中的应用

案例背景

我们的平台需要处理来自以下两个供应商的商品数据:

  1. 供应商 A:提供 JSON 格式的商品数据,需要对商品名称进行标准化处理。
  2. 供应商 B:提供 XML 格式的商品数据,需要对商品价格进行汇率转换。

尽管数据格式和处理逻辑不同,但整体的处理流程是相同的,包括以下步骤:

  1. 数据验证:验证数据的完整性和合法性。
  2. 数据解析:将原始数据解析为内部数据结构。
  3. 数据处理:根据业务需求对数据进行处理。
  4. 数据存储:将处理后的数据存储到数据库中。

UML

在这里插入图片描述

搭建抽象基类 - 数据处理的 “总指挥”

首先,我们创建一个抽象的基类 AbstractProductDataHandler,它将定义整个数据处理流程的骨架。

public abstract class AbstractProductDataHandler {// 模板方法,定义了数据处理的固定流程public final void handleProductData(String rawData) {if (validateData(rawData)) {logStart(rawData);Product product = parseData(rawData);processProduct(product);saveProduct(product);logCompletion(product);} else {handleValidationFailure(rawData);}}// 抽象方法,子类需要实现数据验证逻辑protected abstract boolean validateData(String rawData);// 抽象方法,子类需要实现数据解析逻辑protected abstract Product parseData(String rawData);// 抽象方法,子类需要实现数据处理逻辑protected abstract void processProduct(Product product);// 具体方法,通用的数据存储逻辑private void saveProduct(Product product) {System.out.println("保存商品数据:" + product);// 实际项目中,这里会调用数据库操作}// 具体方法,记录处理开始日志private void logStart(String rawData) {System.out.println("开始处理商品数据,来源:" + getSource());}// 具体方法,记录处理完成日志private void logCompletion(Product product) {System.out.println("商品数据处理完成,结果:" + product);}// 具体方法,处理验证失败的情况protected void handleValidationFailure(String rawData) {System.out.println("数据验证失败:" + rawData);}// 抽象方法,子类需要实现以返回数据来源protected abstract String getSource();
}

子类定制 - 适配不同供应商

接下来,我们为每个供应商创建具体的子类,实现抽象基类中的抽象方法。

供应商 A 的数据处理器

@Component
public class SupplierADataHandler extends AbstractProductDataHandler {@Overrideprotected boolean validateData(String rawData) {// 简单的 JSON 格式验证return rawData != null && rawData.startsWith("{") && rawData.endsWith("}");}@Overrideprotected Product parseData(String rawData) {// 解析 JSON 数据// 实际项目中,可以使用 Jackson 或 Gson 等库Product product = new Product();product.setName("SupplierA Product");product.setPrice(100.0);return product;}@Overrideprotected void processProduct(Product product) {// 对商品名称进行标准化处理product.setName(product.getName().toUpperCase());}@Overrideprotected String getSource() {return "Supplier A";}
}

供应商 B 的数据处理器

@Component
public class SupplierBDataHandler extends AbstractProductDataHandler {@Overrideprotected boolean validateData(String rawData) {// 简单的 XML 格式验证return rawData != null && rawData.startsWith("<") && rawData.endsWith(">");}@Overrideprotected Product parseData(String rawData) {// 解析 XML 数据// 实际项目中,可以使用 JAXB 或 DOM 解析器Product product = new Product();product.setName("SupplierB Product");product.setPrice(200.0);return product;}@Overrideprotected void processProduct(Product product) {// 对商品价格进行汇率转换product.setPrice(product.getPrice() * 0.85); // 假设汇率为 0.85}@Overrideprotected String getSource() {return "Supplier B";}
}

在业务代码中整合运用

最后,我们在业务代码中使用这些数据处理器。通过 Spring 的依赖注入机制,我们可以轻松地切换不同的处理器。

@Service
public class ProductDataProcessingService {@Autowiredprivate SupplierADataHandler supplierAHandler;@Autowiredprivate SupplierBDataHandler supplierBHandler;public void processDataFromSupplierA(String rawData) {supplierAHandler.handleProductData(rawData);}public void processDataFromSupplierB(String rawData) {supplierBHandler.handleProductData(rawData);}
}

8. 总结

模板方法模式通过定义算法的骨架并将某些步骤的具体实现延迟到子类中,提供了一种灵活且可扩展的设计方式。它在代码复用、扩展性和灵活性方面具有显著优势,适用于需要固定流程但允许部分步骤自定义的场景。通过合理使用模板方法模式,可以构建出清晰、可维护且易于扩展的代码架构。
在这里插入图片描述

相关文章:

设计模式 - 行为模式_Template Method Pattern模板方法模式在数据处理中的应用

文章目录 概述1. 核心思想2. 结构3. 示例代码4. 优点5. 缺点6. 适用场景7. 案例&#xff1a;模板方法模式在数据处理中的应用案例背景UML搭建抽象基类 - 数据处理的 “总指挥”子类定制 - 适配不同供应商供应商 A 的数据处理器供应商 B 的数据处理器 在业务代码中整合运用 8. 总…...

Spring Boot框架下的单元测试

1. 什么是单元测试 1.1 基本定义 单元测试(Unit Test) 是对软件开发中最小可测单位&#xff08;例如一个方法或者一个类&#xff09;进行验证的一种测试方式。在 Java 后端的 Spring Boot 项目中&#xff0c;单元测试通常会借助 JUnit、Mockito 等框架对代码中核心逻辑进行快…...

git中文件的状态状态切换

在 Git 中&#xff0c;文件的状态是指文件相对于 Git 仓库的当前情况。以下是一些常见的文件状态及其含义&#xff1a; 未跟踪&#xff08;Untracked&#xff09;&#xff1a; 这是新创建的文件或从其他位置复制过来的文件&#xff0c;Git 还没有开始跟踪这些文件的更改。 这些…...

基于脉冲响应不变法的IIR滤波器设计与MATLAB实现

一、设计原理 脉冲响应不变法是一种将模拟滤波器转换为数字滤波器的经典方法。其核心思想是通过对模拟滤波器的冲激响应进行等间隔采样来获得数字滤波器的单位脉冲响应。 设计步骤&#xff1a; 确定数字滤波器性能指标 将数字指标转换为等效的模拟滤波器指标 设计对应的模拟…...

RabbitMQ快速上手及入门

概念 概念&#xff1a; publisher&#xff1a;生产者&#xff0c;也就是发送消息的一方 consumer&#xff1a;消费者&#xff0c;也就是消费消息的一方 queue&#xff1a;队列&#xff0c;存储消息。生产者投递的消息会暂存在消息队列中&#xff0c;等待消费者处理 exchang…...

自动化构建-make/Makefile 【Linux基础开发工具】

文章目录 一、背景二、Makefile编译过程三、变量四、变量赋值1、""是最普通的等号2、“:” 表示直接赋值3、“?” 表示如果该变量没有被赋值&#xff0c;4、""和写代码是一样的&#xff0c; 五、预定义变量六、函数**通配符** 七、伪目标 .PHONY八、其他常…...

计算机网络之计算机网络的分类

计算机网络可以根据不同的角度进行分类&#xff0c;以下是几种常见的分类方式&#xff1a; 1. 按照规模和范围&#xff1a; 局域网&#xff08;LAN&#xff0c;Local Area Network&#xff09;&#xff1a;覆盖较小范围&#xff08;例如一个建筑物或校园&#xff09;&#xf…...

MySQl的日期时间加

MySQL日期相关_mysql 日期加减-CSDN博客MySQL日期相关_mysql 日期加减-CSDN博客 raise notice 查询目标 site:% model:% date:% target:%,t_shipment_date.site,t_shipment_date.model,t_shipment_date.plant_date,v_date_shipment_qty_target;...

响应式编程与协程

响应式编程与协程的比较 响应式编程的弊端虚拟线程Java线程内核线程的局限性传统线程池的demo虚拟线程的demo 响应式编程的弊端 前面用了几篇文章介绍了响应式编程&#xff0c;它更多的使用少量线程实现线程间解耦和异步的作用&#xff0c;如线程的Reactor模型&#xff0c;主要…...

智能小区物业管理系统推动数字化转型与提升用户居住体验

内容概要 在当今快速发展的社会中&#xff0c;智能小区物业管理系统的出现正在改变传统的物业管理方式。这种系统不仅仅是一种工具&#xff0c;更是一种推动数字化转型的重要力量。它通过高效的技术手段&#xff0c;将物业管理与用户居住体验紧密结合&#xff0c;无疑为社区带…...

从Proxmox VE开始:安装与配置指南

前言 Proxmox Virtual Environment (Proxmox VE) 是一个开源的虚拟化平台&#xff0c;基于Debian Linux&#xff0c;支持KVM虚拟机和LXC容器。它提供了一个强大的Web管理界面&#xff0c;方便用户管理虚拟机、存储、网络等资源。Proxmox VE广泛应用于企业级虚拟化、云计算和开…...

【Docker项目实战】使用Docker部署MinIO对象存储(详细教程)

【Docker项目实战】使用Docker部署MinIO对象存储 前言一、 MinIO介绍1.1 MinIO简介1.2 主要特点1.3 主要使用场景二、本次实践规划2.1 本地环境规划2.2 本次实践介绍三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本四、下载MinIO镜像五、…...

【C++】B2115 密码翻译

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目解析&#x1f4af;1. 老师的做法代码实现&#xff1a;思路解析&#xff1a; &#x1f4af;2. 我的做法代码实现&#xff1a;思路分析&#xff1a; &#x1f4af;3. 老师…...

02.04 数据类型

请写出以下几个数据的类型&#xff1a; 整数 a ----->int a的地址 ----->int* 存放a的数组b ----->int[] 存放a的地址的数组c ----->int*[] b的地址 ----->int* c的地址 ----->int** 指向printf函数的指针d ----->int (*)(const char*, ...) …...

Leetcode—598. 区间加法 II【简单】

2025每日刷题&#xff08;206&#xff09; Leetcode—598. 区间加法 II 实现代码 class Solution { public:int maxCount(int m, int n, vector<vector<int>>& ops) {int ans m * n;int x ops.size();if(ops.empty()) {return ans;}int xm ops[0][0], ym …...

AI浪潮下的IT从业者:危机、机遇与进化之路

目录 0. 前言1. 当前形势&#xff1a;站在十字路口1.1 AI的突飞猛进1.2 行业现状分析 2. 核心应对策略2.1 技术深度与广度的平衡2.2 人机协同的工作模式2.3 持续学习与创新 3. 结语 0. 前言 在人工智能快速发展的今天&#xff0c;IT从业者面临前所未有的挑战与机遇。本文将从实…...

OpenCV:图像轮廓

目录 简述 1. 什么是图像轮廓&#xff1f; 2. 查找图像轮廓 2.1 接口定义 2.2 参数说明 2.3 代码示例 2.4 运行结果 3. 绘制图像轮廓 3.1 接口定义 3.2 参数说明 3.3 代码示例 3.4 运行结果 4. 计算轮廓周长 5. 计算轮廓面积 6. 示例&#xff1a;计算图像轮廓的面…...

洛谷P11655「FAOI-R5」Lovely 139

P11655「FAOI-R5」Lovely 139 题目背景 Update&#xff1a;数据有 0 0&#xff0c;答案为 1&#xff0c;请选手特判以正常通过。 Height ≤ 139 \text{Height}\leq139 Height≤139。 题目描述 对于一个 01 \tt 01 01 串 S S S&#xff08;下标从 1 1 1 开始&#xff09;…...

文字显示省略号

多行文本溢出显示省略号...

Windows图形界面(GUI)-QT-C/C++ - QT Tab Widget

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 一、概述 1.1 什么是 QTabWidget&#xff1f; 1.2 使用场景 二、常见样式 2.1 选项卡式界面 2.2 动态添加和删除选项卡 2.3 自定义选项卡标题和图标 三、属性设置 3.1 添加页面&…...

C++11 多线程 锁与条件变量:mutex、lock_guard、unique_lock 和 condition_variable

文章目录 mutex核心成员函数使用场景 lock_guard功能和特性构造函数使用场景 unique_lock功能和特性构造函数核心成员函数使用场景 lock_guard对比unique_lockcondition_variable核心成员函数使用场景 mutex std::mutex 是 C 标准库中提供的一种互斥量&#xff0c;用于在多线程…...

【Proteus】NE555纯硬件实现LED呼吸灯效果,附源文件,效果展示

本文通过NE555定时器芯片和简单的电容充放电电路,设计了一种纯硬件实现的呼吸灯方案,并借助Proteus仿真软件验证其功能。方案无需编程,成本低且易于实现,适合电子爱好者学习PWM(脉宽调制)和定时器电路原理。 一、呼吸灯原理与NE555功能分析 1. 呼吸灯核心原理 呼吸灯的…...

Cosmos - 世界模型开发平台

文章目录 一、关于 Cosmos主要特点模型家族 二、使用示例1、推理2、后训练 许可证和联系方式 一、关于 Cosmos NVIDIA Cosmos是开发者第一的世界基础模型平台&#xff0c;旨在帮助物理AI开发者更好、更快地构建他们的物理AI系统。宇宙包含 预训练模型&#xff0c;可通过拥抱脸…...

图像分割中根据mask的ROI,去除mask和image中没有勾画ROI层数以外的图像

在分割任务中&#xff0c;一个患者有很多层图像&#xff0c;但是勾画的ROI仅有那么几层。我想去除ROI以外层数的那些没用的图像。这里以一个36张图像的nii格式数据为例 查看一下mask文件中有多少个非0图像 import nibabel as nib import numpy as np# 加载 .nii 文件 file_pat…...

【Java基础-42.3】Java 基本数据类型与字符串之间的转换:深入理解数据类型的转换方法

在 Java 开发中&#xff0c;基本数据类型与字符串之间的转换是非常常见的操作。无论是从用户输入中读取数据&#xff0c;还是将数据输出到日志或界面&#xff0c;都需要进行数据类型与字符串之间的转换。本文将深入探讨 Java 中基本数据类型与字符串之间的转换方法&#xff0c;…...

全栈开发:使用.NET Core WebAPI构建前后端分离的核心技巧(一)

目录 cors解决跨域 依赖注入使用 分层服务注册 缓存方法使用 内存缓存使用 缓存过期清理 缓存存在问题 分布式的缓存 cors解决跨域 前后端分离已经成为一种越来越流行的架构模式&#xff0c;由于跨域资源共享(cors)是浏览器的一种安全机制&#xff0c;它会阻止前端应用…...

springboot使用rabbitmq

使用springboot创建rabbitMQ的链接。 整个项目结构如下&#xff1a; 1.maven依赖 <dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>3.4.1</version> </dependency>application.y…...

Linux——ext2文件系统(二)

Linux——ext2文件系统 ext2文件系统宏观认识一、磁盘分区与格式化二、块组&#xff08;Block Group&#xff09;结构三、文件系统特性 文件名与目录名与inode一、inode的作用原理二、文件与目录名与inode的关系 路径一&#xff0c;路径解析二&#xff0c;路径缓存三&#xff0…...

动手学深度学习-3.2 线性回归的从0开始

以下是代码的逐段解析及其实际作用&#xff1a; 1. 环境设置与库导入 %matplotlib inline import random import torch from d2l import torch as d2l作用&#xff1a; %matplotlib inline&#xff1a;在 Jupyter Notebook 中内嵌显示 matplotlib 图形。random&#xff1a;生成…...

“深度强化学习揭秘:掌握DQN与PPO算法的精髓“

深度Q网络&#xff08;Deep Q-Network&#xff0c;简称DQN&#xff09;是一种结合了Q学习和深度神经网络的强化学习算法。它使用神经网络来近似Q值函数&#xff0c;从而实现对复杂状态空间中的动作选择。DQN的核心思想是通过贝尔曼方程&#xff08;Bellman Equation&#xff09…...