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

news2025/2/4 7:01:03

文章目录

  • 概述
  • 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 {

    @Override
    protected void step2() {
        System.out.println("ConcreteClassA 执行步骤2");
    }
}

// 具体子类B
class ConcreteClassB extends AbstractClass {

    @Override
    protected void step2() {
        System.out.println("ConcreteClassB 执行步骤2");
    }

    @Override
    protected 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 {

    @Override
    protected boolean validateData(String rawData) {
        // 简单的 JSON 格式验证
        return rawData != null && rawData.startsWith("{") && rawData.endsWith("}");
    }

    @Override
    protected Product parseData(String rawData) {
        // 解析 JSON 数据
        // 实际项目中,可以使用 Jackson 或 Gson 等库
        Product product = new Product();
        product.setName("SupplierA Product");
        product.setPrice(100.0);
        return product;
    }

    @Override
    protected void processProduct(Product product) {
        // 对商品名称进行标准化处理
        product.setName(product.getName().toUpperCase());
    }

    @Override
    protected String getSource() {
        return "Supplier A";
    }
}

供应商 B 的数据处理器

@Component
public class SupplierBDataHandler extends AbstractProductDataHandler {

    @Override
    protected boolean validateData(String rawData) {
        // 简单的 XML 格式验证
        return rawData != null && rawData.startsWith("<") && rawData.endsWith(">");
    }

    @Override
    protected Product parseData(String rawData) {
        // 解析 XML 数据
        // 实际项目中,可以使用 JAXB 或 DOM 解析器
        Product product = new Product();
        product.setName("SupplierB Product");
        product.setPrice(200.0);
        return product;
    }

    @Override
    protected void processProduct(Product product) {
        // 对商品价格进行汇率转换
        product.setPrice(product.getPrice() * 0.85); // 假设汇率为 0.85
    }

    @Override
    protected String getSource() {
        return "Supplier B";
    }
}

在业务代码中整合运用

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

@Service
public class ProductDataProcessingService {

    @Autowired
    private SupplierADataHandler supplierAHandler;

    @Autowired
    private SupplierBDataHandler supplierBHandler;

    public void processDataFromSupplierA(String rawData) {
        supplierAHandler.handleProductData(rawData);
    }

    public void processDataFromSupplierB(String rawData) {
        supplierBHandler.handleProductData(rawData);
    }
}

8. 总结

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

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

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

相关文章

基于脉冲响应不变法的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八、其他常…

响应式编程与协程

响应式编程与协程的比较 响应式编程的弊端虚拟线程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广泛应用于企业级虚拟化、云计算和开…

【C++】B2115 密码翻译

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

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 …

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;计算图像轮廓的面…

文字显示省略号

多行文本溢出显示省略号

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…

全栈开发:使用.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…

如何让DeepSeek恢复联网功能?解决(由于技术原因,联网搜索暂不可用)

DeekSeek提示&#xff1a;&#xff08;由于技术原因&#xff0c;联网搜索暂不可用&#xff09; 众所周知&#xff0c;因为海外黑客的ddos攻击、僵尸网络攻击&#xff0c;deepseek的联网功能一直处于宕机阶段&#xff0c;但是很多问题不联网出来的结果都还是2023年的&#xff0c…

python的ruff简单使用

Ruff 是一个用 Rust 编写的高性能 Python 静态分析工具和代码格式化工具。它旨在提供快速的代码检查和格式化功能&#xff0c;同时支持丰富的配置选项和与现有工具的兼容性。ruff是用rust实现的python Linter&Formatter。 安装&#xff1a; conda install -c conda-forge…

【漫话机器学习系列】077.范数惩罚是如何起作用的(How Norm Penalties Work)

范数惩罚的作用与原理 范数惩罚&#xff08;Norm Penalty&#xff09; 是一种常用于机器学习模型中的正则化技术&#xff0c;它的主要目的是控制模型复杂度&#xff0c;防止过拟合。通过对模型的参数进行惩罚&#xff08;即在损失函数中加入惩罚项&#xff09;&#xff0c;使得…

LLMs之OpenAI o系列:OpenAI o3-mini的简介、安装和使用方法、案例应用之详细攻略

LLMs之OpenAI o系列&#xff1a;OpenAI o3-mini的简介、安装和使用方法、案例应用之详细攻略 目录 相关文章 LLMs之o3&#xff1a;《Deliberative Alignment: Reasoning Enables Safer Language Models》翻译与解读 LLMs之OpenAI o系列&#xff1a;OpenAI o3-mini的简介、安…

Notepad++消除生成bak文件

设置(T) ⇒ 首选项... ⇒ 备份 ⇒ 勾选 "禁用" 勾选禁用 就不会再生成bak文件了 notepad怎么修改字符集编码格式为gbk 如图所示