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

C++软件设计模式之模板方法模式

模板方法模式是面向对象软件设计模式之一,其主要意图是在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的情况下重新定义算法的某些特定步骤。

动机

在软件开发中,常常会遇到这样的情况:一个任务可以被分解为多个步骤来完成,其中有些步骤的实现方法是对所有子类通用的,而有些步骤则需要针对不同的子类有不同的实现方式。模板方法模式正是解决这一问题的有效手段。通过使用模板方法模式,可以将不变的部分代码抽离出来放在基类中,而将可变的部分留给子类去实现。这样不仅减少了代码的重复,而且还能够保证变与不变的部分之间的联系。

意图

模板方法模式的主要意图是定义一个操作中的算法的骨架,而将一些步骤的实现延迟到子类中。这样做可以让子类在不改变算法整体结构的前提下去重写算法的某些特定部分。

适用场合

  1. 不变的算法骨架:当某个抽象类有多个子类,但是它们有共同的算法步骤,只是某些步骤的具体实现不同,可以通过模版方法模式将这些不变的部分提取到抽象类中,子类只需要实现特定的步骤。
  2. 避免代码重复:多个类中若有一段代码完全相同或相似,可以考虑使用模版方法模式来抽取共享的部分,减少代码重复。
  3. 控制子类扩展:模版方法模式可以通过在模版方法中调用“钩子”方法,来控制子类在生命周期中的特定点上能够做什么,不能够做什么。钩子方法在抽象类中通常是空实现,子类可以覆盖这些方法来实现特定功能。

示例

模板方法模式的一个经典示例是咖啡店和茶馆的饮料制作过程。制作咖啡和茶的基本流程大致相同(准备热水、冲泡、倒入杯子、加调料),但具体的步骤(如冲泡的方式、加何种调料)则不同。通过定义一个模版方法来实现这个流程,基类可以控制整个流程的执行顺序,而具体的冲泡和加调料动作则由子类实现。

模板方法模式是一种非常有用的设计模式,在很多框架和库中都有应用。正确地使用模版方法模式可以使代码更加清晰、易于扩展和维护。

面是一个简单的C++示例,展示了如何使用模板方法模式来实现咖啡和茶的制作过程。在这个例子中,基类 Beverage 定义了一个模板方法 prepareRecipe(),该方法包含了制作饮料的通用步骤,而具体的步骤(如冲泡和添加调料)则由子类实现。

代码示例

#include <iostream>
using namespace std;// 基类:Beverage
class Beverage {
public:// 模板方法,定义了制作饮料的算法骨架void prepareRecipe() {boilWater();brew();pourInCup();addCondiments();}// 子类不需要覆盖的方法,因为这些步骤对所有饮料都是相同的void boilWater() {cout << "Boiling water" << endl;}void pourInCup() {cout << "Pouring into cup" << endl;}// 子类必须覆盖的方法,因为这些步骤对不同饮料是不同的virtual void brew() = 0; // 纯虚函数,必须在子类中实现virtual void addCondiments() = 0; // 纯虚函数,必须在子类中实现
};// 子类:Coffee
class Coffee : public Beverage {
public:void brew() override {cout << "Dripping Coffee through filter" << endl;}void addCondiments() override {cout << "Adding Sugar and Milk" << endl;}
};// 子类:Tea
class Tea : public Beverage {
public:void brew() override {cout << "Steeping the tea" << endl;}void addCondiments() override {cout << "Adding Lemon" << endl;}
};int main() {cout << "Making Coffee..." << endl;Coffee coffee;coffee.prepareRecipe();cout << "\nMaking Tea..." << endl;Tea tea;tea.prepareRecipe();return 0;
}

运行结果

Making Coffee...
Boiling water
Dripping Coffee through filter
Pouring into cup
Adding Sugar and MilkMaking Tea...
Boiling water
Steeping the tea
Pouring into cup
Adding Lemon

解释

  1. 基类 Beverage:

    • prepareRecipe() 是模板方法,定义了制作饮料的步骤:煮水、冲泡、倒入杯子、加调料。
    • boilWater() 和 pourInCup() 是所有饮料共有的步骤,因此在基类中实现。
    • brew() 和 addCondiments() 是纯虚函数,需要在子类中实现,因为这些步骤对不同的饮料有不同的实现。
  2. 子类 Coffee 和 Tea:

    • Coffee 子类实现了 brew() 和 addCondiments(),分别表示冲泡咖啡和添加糖和牛奶。
    • Tea 子类实现了 brew() 和 addCondiments(),分别表示泡茶和添加柠檬。
  3. main 函数:

    • 创建 Coffee 和 Tea 对象,并调用它们的 prepareRecipe() 方法,输出了制作咖啡和茶的过程。

适用场合

  • 通用流程,具体步骤不同:当多个子类需要遵循相同的流程,但具体的步骤实现不同,可以使用模板方法模式。例如,不同的支付方式(信用卡、支付宝、微信)可以遵循相同的支付流程,但具体的支付接口和验证方式不同。
  • 避免重复代码:在多个子类中存在相同的逻辑时,可以将这些逻辑提取到基类的模板方法中,避免代码重复。

模板方法模式通过将算法的结构固定,允许子类灵活地实现具体步骤,提供了一种优雅的方式来处理类似的任务。

 模板方法模式经常与其他设计模式协同使用,以解决更复杂的设计问题。常见的协同模式包括策略模式、工厂方法模式和状态模式等。下面我们将重点介绍模板方法模式与策略模式的协同使用,并给出一个C++代码示例。

模板方法模式与策略模式的协同使用

动机

模板方法模式用于定义一个算法的骨架,而将某些步骤的实现延迟到子类中。策略模式用于定义一系列可互换的算法,并将这些算法封装在独立的类中。通过将策略模式与模板方法模式结合,可以在一个模板方法中使用不同的策略,从而提供更大的灵活性。

适用场景
  1. 算法的某些步骤需要根据不同的条件动态选择不同的实现:可以通过策略模式将这些步骤的实现封装在不同的策略类中,然后在模板方法中根据需要选择合适的策略。
  2. 需要在运行时动态改变算法的某些步骤:策略模式允许在运行时动态更换算法,而模板方法模式确保了算法的整体结构不变。

代码示例

假设我们有一个任务是处理数据,不同的处理策略可以根据不同的需求进行选择。我们使用模板方法模式来定义数据处理的骨架,使用策略模式来实现不同的处理策略。

1. 定义策略接口和具体策略
// 策略接口
class DataProcessingStrategy {
public:virtual void process() = 0;virtual ~DataProcessingStrategy() = default;
};// 具体策略1:压缩数据
class CompressStrategy : public DataProcessingStrategy {
public:void process() override {cout << "Compressing data" << endl;}
};// 具体策略2:加密数据
class EncryptStrategy : public DataProcessingStrategy {
public:void process() override {cout << "Encrypting data" << endl;}
};// 具体策略3:校验数据
class VerifyStrategy : public DataProcessingStrategy {
public:void process() override {cout << "Verifying data" << endl;}
};

2. 定义抽象类和模板方法
// 抽象类
class DataProcessor {
protected:DataProcessingStrategy* strategy;public:DataProcessor(DataProcessingStrategy* s) : strategy(s) {}virtual ~DataProcessor() {delete strategy;}// 模板方法void processData() {load();strategy->process();save();}virtual void load() {cout << "Loading data" << endl;}virtual void save() {cout << "Saving data" << endl;}
};

3. 定义具体的数据处理器
// 具体的数据处理器1:使用压缩策略
class CompressDataProcessor : public DataProcessor {
public:CompressDataProcessor() : DataProcessor(new CompressStrategy()) {}
};// 具体的数据处理器2:使用加密策略
class EncryptDataProcessor : public DataProcessor {
public:EncryptDataProcessor() : DataProcessor(new EncryptStrategy()) {}
};// 具体的数据处理器3:使用校验策略
class VerifyDataProcessor : public DataProcessor {
public:VerifyDataProcessor() : DataProcessor(new VerifyStrategy()) {}
};

4. 客户端代码
#include <iostream>
using namespace std;int main() {// 使用压缩策略处理数据cout << "Using Compress Strategy..." << endl;DataProcessor* processor1 = new CompressDataProcessor();processor1->processData();delete processor1;// 使用加密策略处理数据cout << "\nUsing Encrypt Strategy..." << endl;DataProcessor* processor2 = new EncryptDataProcessor();processor2->processData();delete processor2;// 使用校验策略处理数据cout << "\nUsing Verify Strategy..." << endl;DataProcessor* processor3 = new VerifyDataProcessor();processor3->processData();delete processor3;return 0;
}

运行结果

Using Compress Strategy...
Loading data
Compressing data
Saving dataUsing Encrypt Strategy...
Loading data
Encrypting data
Saving dataUsing Verify Strategy...
Loading data
Verifying data
Saving data

解释

  1. 策略接口 DataProcessingStrategy:

    • 定义了一个纯虚函数 process(),用于实现数据处理的具体策略。
    • 具体策略 CompressStrategyEncryptStrategy 和 VerifyStrategy 分别实现了数据压缩、加密和校验的策略。
  2. 抽象类 DataProcessor:

    • 包含一个策略对象 strategy,并通过构造函数传递具体的策略对象。
    • 定义了模板方法 processData(),该方法调用了 load()strategy->process() 和 save(),确保数据处理的流程一致。
    • load() 和 save() 是通用的数据加载和保存步骤,可以在子类中根据需要进行扩展。
  3. 具体数据处理器类:

    • CompressDataProcessorEncryptDataProcessor 和 VerifyDataProcessor 分别使用不同的策略对象初始化 DataProcessor
  4. 客户端代码:

    • 创建不同的数据处理器对象,并调用 processData() 方法来处理数据,输出了不同的处理策略的结果。

通过这种方式,模板方法模式和策略模式的结合提供了更大的灵活性,允许在运行时动态选择不同的数据处理策略,同时保持数据处理流程的一致性。

相关文章:

C++软件设计模式之模板方法模式

模板方法模式是面向对象软件设计模式之一&#xff0c;其主要意图是在一个方法中定义一个算法的骨架&#xff0c;而将一些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的情况下重新定义算法的某些特定步骤。 动机 在软件开发中&#xff0c;常常会遇到这样的情…...

神经网络的初始化方式都有哪些?

一、概念 神经网络的初始化是深度学习中的一个关键步骤&#xff0c;它指的是在训练开始前为神经网络的权重和偏置设置初始值。合适的初始化方法可以加速模型的收敛&#xff0c;提高训练效果&#xff0c;甚至影响模型的最终性能。当然&#xff0c;目前我们使用Torch、TensorFlow…...

const成员函数

在c中经常看到这样的声明&#xff1a; class A{ ... int fun1() const; //const成员函数 int fun2() const; //const成员函数private: int a; //属于状态 static int b; //不属于状态&#xff0c;属于类 } 这个const关键字声明了这个函数是const成员函数&#xff0c;con…...

物理知识1——电流

说起电流&#xff0c;应该从电荷说起&#xff0c;而说起电荷&#xff0c;应该从原子说起。 1 原子及其结构 常见的物质是由分子构成的&#xff0c;而分子又是由原子构成的&#xff0c;有的分子是由多个原子构成&#xff0c;有的分子只由一个原子构成。而原子的构成如图1所示。…...

车载通信架构 --- 智能汽车通信前沿技术

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所谓鸡汤,要么蛊惑你认命,要么怂恿你拼命,但都是回避问题的根源,以现象替代逻辑,以情绪代替思考,把消极接受现实的懦弱,伪装成乐观面对不幸的…...

Flutter中添加全局防护水印的实现

随着版权意识的加强&#xff0c;越来越多的应用开始在应用内部增加各种各样的水印信息&#xff0c;防止核心信息泄露&#xff0c;便于朔源。 效果如下&#xff1a; 在Flutter中增加全局水印的方式&#xff0c;目前有两种实现。 方案一&#xff0c;在native层添加一个遮罩层&a…...

BGP(Border Gateway Protocol)路由收集器

全球 BGP&#xff08;边界网关协议&#xff09;路由收集器的分布情况以及相关数据。以下是主要的信息解读&#xff1a; 地图标记&#xff1a; 每个绿色点代表一个路由收集器的位置。路由收集器分布在全球不同的地区&#xff0c;覆盖了五大区域&#xff1a; ARIN&#xff08;美…...

【DAGMM】直接跑tip

1.from sklearn.externals import joblib 版本高 joblib没有 直接pip install joblib&#xff0c;然后 import joblib 2.AttributeError: module ‘tensorflow’ has no attribute ‘set_random_seed’ # tf.set_random_seed(args.seed)#tf<2.0 tf.random.set_seed(args.s…...

vscode中调用deepseek实现AI辅助编程

来自 Python大数据分析 费弗里 1 简介 大家好我是费老师&#xff0c;最近国产大模型Deepseek v3新版本凭借其优秀的模型推理能力&#xff0c;讨论度非常之高&#x1f525;&#xff0c;且其官网提供的相关大模型API接口服务价格一直走的“价格屠夫”路线&#xff0c;性价比很高…...

AI大模型语音识别转文字

提取音频 本项目作用在于将常见的会议录音文件、各种语种音频文件进行转录成相应的文字&#xff0c;也可从特定视频中提取对应音频进行转录成文字保存在本地。最原始的从所给网址下载对应视频和音频进行处理。下载ffmpeg(https://www.gyan.dev/ffmpeg/builds/packages/ffmpeg-…...

可由 (5V) 单片机直接驱动的模块

可由 &#xff08;5V&#xff09; 单片机 直接驱动的模块 1. 传感器类 元器件描述温度传感器DS18B20&#xff08;数字温度传感器&#xff09;光强传感器光敏电阻&#xff08;通过 ADC 读取&#xff09;红外传感器红外接收模块&#xff08;如 VS1838&#xff09;超声波传感器HC…...

vue使用树形结构展示文件和文件夹

1. 树形结构显示 显示文件夹和文件&#xff1a;使用 el-tree 组件展示树形结构&#xff0c;文件夹和文件的图标通过 el-icon 进行动态显示。文件夹使用 Folder 图标&#xff0c;文件使用 Files 图标。节点点击&#xff1a;点击树形节点后&#xff0c;会将选中的节点保存到 sel…...

PHP框架+gatewayworker实现在线1对1聊天--聊天界面布局+创建websocket连接(5)

文章目录 聊天界面布局html代码 创建websocket连接为什么要绑定&#xff1f; 聊天界面布局 在View/Index目录下创建index.html html代码 <div id"chat"><div id"nbar"><div class"pull-left">与牛德胜正在聊天...</div…...

LinuxUbuntu打开VSCode白屏解决方案

解决方法是 以root权限打开VSCode sudo /usr/share/code/code --no-sandbox --unity-launch...

在 ESP 上运行 AWTK

AWTK 基于 esp 的移植。 测试硬件平台为 ESP32-S3-Touch-LCD-4.3&#xff0c;其它平台请根据实际平台自行调整。 安装下载工具 建议下载离线版本 ESP IDF v5.3.2 下载代码 git clone https://github.com/zlgopen/awtk-esp.git cd awtk-esp git clone https://github.com/zlg…...

硬件工程师面试题 21-30

把常见的硬件面试题进行总结&#xff0c;方便及时巩固复习。其中包括网络上的资源、大佬们的大厂面试题&#xff0c;其中可能会题目类似&#xff0c;加强印象即可。 更多硬件面试题&#xff1a;硬件工程师面试题 1-10硬件工程师面试题 11-20 21、单片机最小系统需要什么&#x…...

开源架构的容器化部署优化版

上三篇文章推荐&#xff1a; 开源架构的微服务架构实践优化版&#xff08;New&#xff09; 开源架构中的数据库选择优化版&#xff08;New&#xff09; 开源架构学习指南&#xff1a;文档与资源的智慧锦囊&#xff08;New&#xff09; 我管理的社区推荐&#xff1a;【青云交社区…...

Qt使用CMake编译项目时报错:#undefined reference to `vtable for MainView‘

博主将.h文件和.cpp文件放到了不同的文件目录下面&#xff0c;如下图所示&#xff1a; 于是构建项目的时候就报错了#undefined reference to vtable for MainView&#xff0c;这个是由于src/view目录下的CMake无法自动moc头文件导致的&#xff0c;需要手动moc include/view目录…...

python学习笔记—12—

1. 布尔类型 (1) 定义 (2) 比较运算符 (3) 代码演示 1. 手动定义 bool_1 True bool_2 False print(f"bool_1的内容是&#xff1a;{bool_1}, 类型是&#xff1a;{type(bool_1)}") print(f"bool_2的内容是&#xff1a;{bool_2}, 类型是&#xff1a;{type(bool…...

==和===的区别,被坑的一天

在 JavaScript 中&#xff0c; 和 都用于比较两个值&#xff0c;但它们有一个重要的区别&#xff1a; 1. (宽松相等运算符) 进行比较时&#xff0c;会 自动类型转换&#xff08;也叫做强制类型转换&#xff09;&#xff0c;即如果比较的两个值的类型不同&#xff0c;JavaScr…...

Java编程避坑指南:九大类常见陷阱与解决方案,助你写出高质量代码

文章目录 基础类 类、继承与内存 继承特性与注意事项 内存管理 现代 Java 特性 记录类与密封类常见陷阱 集合与遍历 相等性约定 集合常见陷阱 并发与同步 并发 异常处理 泛型与类型擦除 泛型陷阱 泛型与类型擦除 泛型陷阱 JVM、垃圾回收与模块系统 JVM/GC 常见陷阱 模块系统(J…...

Phi-4-mini-reasoning保姆级教程:14GB显存跑通128K长上下文推理

Phi-4-mini-reasoning保姆级教程&#xff1a;14GB显存跑通128K长上下文推理 1. 模型介绍 Phi-4-mini-reasoning是微软推出的3.8B参数轻量级开源模型&#xff0c;专为数学推理、逻辑推导和多步解题等强逻辑任务设计。这个模型主打"小参数、强推理、长上下文、低延迟"…...

镜像视界|AI智能体驱动的无感定位系统:从识别到控制的跃迁副标题:融合行为建模与轨迹预测的空间级目标管理体系

镜像视界&#xff5c;AI智能体驱动的无感定位系统&#xff1a;从识别到控制的跃迁——融合行为建模与轨迹预测的空间级目标管理体系一、范式升级&#xff1a;AI正在从“工具”进化为“智能体”在传统视频与AI系统中&#xff0c;人工智能的角色长期被定义为“工具”&#xff1a;…...

Windows 10下SQLMap安装配置全攻略(附Python环境搭建)

Windows 10下SQLMap实战指南&#xff1a;从Python环境搭建到高级配置 在渗透测试和安全研究领域&#xff0c;SQLMap无疑是数据库安全检测的瑞士军刀。这款开源工具能够自动检测和利用SQL注入漏洞&#xff0c;支持几乎所有主流数据库系统。但对于Windows用户&#xff0c;特别是刚…...

高效构建个性化Obsidian知识管理系统:从模板到实践的全面指南

高效构建个性化Obsidian知识管理系统&#xff1a;从模板到实践的全面指南 【免费下载链接】kepano-obsidian My personal Obsidian vault template. A bottom-up approach to note-taking and organizing things I am interested in. 项目地址: https://gitcode.com/gh_mirro…...

ERNIE-4.5-0.3B-PT创意写作实战:用AI帮你生成有画面感的场景描述

ERNIE-4.5-0.3B-PT创意写作实战&#xff1a;用AI帮你生成有画面感的场景描述 1. 为什么需要AI辅助创意写作 在内容创作领域&#xff0c;最令人头疼的莫过于如何让文字"活起来"。传统写作往往面临三大痛点&#xff1a; 画面感不足&#xff1a;描述停留在表面&#…...

Windows右键菜单终极清理指南:3步让你的右键菜单重获新生

Windows右键菜单终极清理指南&#xff1a;3步让你的右键菜单重获新生 【免费下载链接】ContextMenuManager &#x1f5b1;️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 还在为每次右键点击文件时弹出的杂乱菜单而…...

5分钟掌握gdrivedl:突破Google Drive下载限制的高效工具

5分钟掌握gdrivedl&#xff1a;突破Google Drive下载限制的高效工具 【免费下载链接】gdrivedl Google Drive Download Python Script 项目地址: https://gitcode.com/gh_mirrors/gd/gdrivedl 解决云存储下载痛点&#xff1a;为什么你需要这款工具&#xff1f; 作为经常…...

Android显示驱动避坑指南:高通平台UEFI显示初始化常见问题解析

Android显示驱动避坑指南&#xff1a;高通平台UEFI显示初始化常见问题解析 在移动设备开发领域&#xff0c;显示系统的稳定性直接影响用户体验。作为Android底层开发的核心环节&#xff0c;高通平台UEFI显示初始化过程涉及硬件抽象层、固件配置和内核交互等多个技术层面。本文…...

怎么样辨别生活中遇到的那些理财平台的真假?

怎么样辨别生活中遇到的那些理财平台的真假&#xff1f;凡是声称高息保本的投资理财平台极有可能是黑平台。尝试用手机官方应用商城搜索理财软件&#xff0c;如果是别人通过聊天软件发链接给你安装的&#xff0c;不是正规手机应用商城下载的&#xff0c;且在应用商城无法搜索到…...