突破编程_C++_设计模式(模板方法模式)

1 模板方法模式的基本概念

C++ 模板方法模式是一种行为设计模式,它在一个操作中定义算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下重新定义算法的某些特定步骤。在C++中,模板方法模式通常通过使用虚函数和继承来实现。

模板方法模式包括两个主要部分:

(1)抽象类(Abstract Class): 定义了一个或多个操作,其中一些是具体的(由基类实现),而另一些是抽象的(在子类中实现)。这个抽象类还定义了一个模板方法,该方法调用这些操作。模板方法通常被声明为非虚函数,并在基类中实现,以确保其调用顺序不会被子类改变。

(2)具体子类(Concrete Subclass): 继承自抽象类,并实现其中的抽象操作。子类通过提供这些操作的具体实现来扩展算法。

2 模板方法模式的实现步骤

模板方法模式的实现步骤如下:

(1)定义抽象类: 首先,需要定义一个抽象类(或者接口),这个类包含了一个或多个抽象方法(纯虚函数),这些方法在抽象类中没有被实现,需要在子类中实现。同时,这个抽象类还包含一个模板方法,这个方法在抽象类中已经被实现,它调用了那些抽象方法。

(2)实现子类: 然后,需要创建继承自抽象类的子类,并实现那些抽象方法。子类可以重写模板方法,但通常模板方法已经在抽象类中得到了完整的实现,所以子类只需要关注那些抽象方法的实现即可。

(3)使用模板方法: 最后,可以创建子类的实例,并调用模板方法。模板方法会按照其在抽象类中定义的顺序调用那些抽象方法,而这些抽象方法的具体实现则取决于创建的是哪个子类的实例。

如下为样例代码:

#include <iostream>  
#include <string>  
#include <memory>  class AbstractClass 
{
public:virtual ~AbstractClass() {}// 模板方法  void templateMethod() {specificMethod1();specificMethod2();}// 抽象方法,需要在子类中实现  virtual void specificMethod1() = 0;virtual void specificMethod2() = 0;
};class ConcreteClass : public AbstractClass
{
public:// 实现抽象方法  void specificMethod1() override {std::cout << "ConcreteClass::specificMethod1() " << std::endl;}void specificMethod2() override {std::cout << "ConcreteClass::specificMethod2() " << std::endl;}
};int main() 
{std::shared_ptr<ConcreteClass> concreteClass = std::make_shared<ConcreteClass>();concreteClass->templateMethod();  // 调用模板方法  return 0;
}

上面代码的输出为:

ConcreteClass::specificMethod1()
ConcreteClass::specificMethod2()

上面代码就是在 C++ 中实现模板方法模式的基本步骤。通过这种方式,可以在不改变算法结构的情况下,通过改变子类的实现来改变算法的行为。

3 模板方法模式的应用场景

C++模板方法模式的应用场景通常出现在那些需要定义一个操作中的算法的骨架,而将一些步骤延迟到子类中实现的情况。具体来说,当多个类有相同的方法,并且逻辑相同,只是细节上有差异时,可以考虑使用模板方法模式。以下是一些具体的应用场景:

(1)统一操作步骤但细节不同: 当多个应用场景具有相同的操作步骤或过程,但具体的操作细节却各不相同时,可以使用模板方法模式。例如,在不同的业务系统中,可能存在类似的业务流程,但某些具体步骤或处理方式却有所不同。此时,可以在抽象类中定义业务流程的骨架,而将具体的处理步骤留给子类去实现。

(2)重构现有代码: 当在重构代码时,如果发现有大量的重复代码,可以将这些重复的代码抽取到父类中,通过模板方法模式来约束其行为。这样不仅可以减少代码的重复,还可以提高代码的可维护性和可读性。

(3)扩展性要求较高的场景: 如果系统需要支持多种不同的业务逻辑,并且这些业务逻辑之间有一定的相似性,那么可以使用模板方法模式。通过定义抽象类和子类,可以方便地添加新的业务逻辑,而无需修改已有的代码。

在使用模板方法模式时,需要注意的是,由于每一个不同的实现都需要一个子类来实现,这可能导致类的个数增加,使得系统更加庞大。因此,在设计时需要权衡利弊,根据具体的需求和场景来选择合适的设计模式。

总的来说,C++ 模板方法模式在统一操作步骤但细节不同的场景、重构现有代码以及扩展性要求较高的场景中都有广泛的应用。

3.1 模板方法模式应用于统一操作步骤但细节不同的场景

假设有一个在线支付系统,其中有多种支付方式(如信用卡支付、支付宝支付等),每种支付方式都有相同的支付流程(如验证支付信息、执行支付操作、记录支付日志等),但具体的验证和支付细节是不同的。

首先,定义一个抽象类 PaymentProcessor,它包含模板方法 processPayment 以及抽象方法 validatePaymentInfo 和 executePayment:

#include <iostream>  
#include <string>  
#include <memory>  class PaymentProcessor 
{
public:virtual ~PaymentProcessor() = default;// 模板方法,定义支付流程的骨架  void processPayment(const std::string& paymentInfo) {if (validatePaymentInfo(paymentInfo)) {if (executePayment()) {recordPaymentLog(paymentInfo);std::cout << "Payment processed successfully." << std::endl;}else {std::cout << "Payment execution failed." << std::endl;}}else {std::cout << "Invalid payment information." << std::endl;}}// 抽象方法,需要在子类中实现  virtual bool validatePaymentInfo(const std::string& paymentInfo) = 0;virtual bool executePayment() = 0;// 通用方法,记录支付日志(可以是抽象方法,这里直接实现作为示例)  void recordPaymentLog(const std::string& paymentInfo) {std::cout << "Payment log recorded for info: " << paymentInfo << std::endl;}
};

接下来,创建两个继承自 PaymentProcessor 的子类 CreditCardProcessor 和 AlipayProcessor,它们分别实现了信用卡支付和支付宝支付的细节:

class CreditCardProcessor : public PaymentProcessor {
public:bool validatePaymentInfo(const std::string& paymentInfo) override {// 信用卡支付信息的验证逻辑  std::cout << "Validating credit card payment info..." << std::endl;// 假设验证总是成功  return true;}bool executePayment() override {// 执行信用卡支付操作逻辑  std::cout << "Executing credit card payment..." << std::endl;// 假设支付总是成功  return true;}
};class AlipayProcessor : public PaymentProcessor 
{
public:bool validatePaymentInfo(const std::string& paymentInfo) override {// 支付宝支付信息的验证逻辑  std::cout << "Validating Alipay payment info..." << std::endl;// 假设验证总是成功  return true;}bool executePayment() override {// 执行支付宝支付操作逻辑  std::cout << "Executing Alipay payment..." << std::endl;// 假设支付总是成功  return true;}
};

最后,在 main 函数中,使用智能指针来管理这些支付处理器的生命周期,并调用模板方法 processPayment 来执行支付操作:

int main() 
{// 使用智能指针管理支付处理器的生命周期  std::unique_ptr<PaymentProcessor> creditCardProcessor = std::make_unique<CreditCardProcessor>();std::unique_ptr<PaymentProcessor> alipayProcessor = std::make_unique<AlipayProcessor>();// 执行信用卡支付  creditCardProcessor->processPayment("Credit card payment info");// 执行支付宝支付  alipayProcessor->processPayment("Alipay payment info");// 智能指针在离开作用域时会自动释放对象  return 0;
}

上面这些代码的输出为:

Validating credit card payment info...
Executing credit card payment...
Payment log recorded for info: Credit card payment info
Payment processed successfully.
Validating Alipay payment info...
Executing Alipay payment...
Payment log recorded for info: Alipay payment info
Payment processed successfully.

这个简单的示例定义了一个通用的支付流程(processPayment 方法),它包含了验证支付信息、执行支付操作和记录支付日志等步骤。每种支付方式(信用卡和支付宝)都提供了自己特定的验证和支付逻辑,通过继承 PaymentProcessor 类并实现相应的抽象方法来实现。使用智能指针来管理这些对象的生命周期确保了资源的自动释放。

3.2 模板方法模式应用于重构现有代码

假设有一个处理用户订单的系统,它包含多个步骤,如验证订单、处理支付、更新库存和发送确认邮件。这些步骤中的大部分对于不同类型的订单是相似的,但有一些步骤的具体实现是不同的。这种场景使用使用模板方法模式来重构这个系统,使得它更加灵活和易于维护。

首先,定义一个抽象基类 OrderProcessor,它包含了模板方法 processOrder 和几个抽象方法,每个抽象方法对应一个步骤:

#include <iostream>  
#include <memory>  
#include <string>  class OrderProcessor
{
public:virtual ~OrderProcessor() = default;// 模板方法,定义处理订单的通用流程  void processOrder(const std::string& orderInfo) {validateOrder(orderInfo);processPayment(orderInfo);updateInventory(orderInfo);sendConfirmationEmail(orderInfo);}// 抽象方法,由子类实现具体的验证逻辑  virtual void validateOrder(const std::string& orderInfo) = 0;// 抽象方法,由子类实现具体的支付处理逻辑  virtual void processPayment(const std::string& orderInfo) = 0;// 抽象方法,由子类实现具体的库存更新逻辑  virtual void updateInventory(const std::string& orderInfo) = 0;// 抽象方法,由子类实现具体的发送确认邮件逻辑  virtual void sendConfirmationEmail(const std::string& orderInfo) = 0;
};

接下来,创建具体的订单处理器类,它们继承自 OrderProcessor 并实现具体的步骤:

class StandardOrderProcessor : public OrderProcessor {
public:void validateOrder(const std::string& orderInfo) override {std::cout << "Validating standard order: " << orderInfo << std::endl;// 实现具体的验证逻辑  }void processPayment(const std::string& orderInfo) override {std::cout << "Processing payment for standard order: " << orderInfo << std::endl;// 实现具体的支付处理逻辑  }void updateInventory(const std::string& orderInfo) override {std::cout << "Updating inventory for standard order: " << orderInfo << std::endl;// 实现具体的库存更新逻辑  }void sendConfirmationEmail(const std::string& orderInfo) override {std::cout << "Sending confirmation email for standard order: " << orderInfo << std::endl;// 实现具体的发送确认邮件逻辑  }
};class CustomOrderProcessor : public OrderProcessor 
{
public:void validateOrder(const std::string& orderInfo) override {std::cout << "Validating custom order: " << orderInfo << std::endl;// 实现自定义订单的验证逻辑  }// 假设自定义订单不需要处理支付  void processPayment(const std::string& orderInfo) override {std::cout << "Custom order does not require payment processing." << std::endl;}void updateInventory(const std::string& orderInfo) override {std::cout << "Updating inventory for custom order: " << orderInfo << std::endl;// 实现自定义订单的库存更新逻辑  }void sendConfirmationEmail(const std::string& orderInfo) override {std::cout << "Sending special confirmation email for custom order: " << orderInfo << std::endl;// 实现自定义订单的发送确认邮件逻辑  }
};

最后,在 main 函数中,使用智能指针来管理这些订单处理器的生命周期,并调用模板方法 processOrder 来处理不同类型的订单:

int main() 
{// 使用智能指针管理订单处理器的生命周期  std::unique_ptr<OrderProcessor> standardOrder = std::make_unique<StandardOrderProcessor>();std::unique_ptr<OrderProcessor> customOrder = std::make_unique<CustomOrderProcessor>();// 处理标准订单  standardOrder->processOrder("Standard order details");// 处理自定义订单  customOrder->processOrder("Custom order details");// 智能指针在离开作用域时会自动释放对象  return 0;
}

上面这些代码的输出为:

Validating standard order: Standard order details
Processing payment for standard order: Standard order details
Updating inventory for standard order: Standard order details
Sending confirmation email for standard order: Standard order details
Validating custom order: Custom order details
Custom order does not require payment processing.
Updating inventory for custom order: Custom order details
Sending special confirmation email for custom order: Custom order details

在上述重构示例中,定义了一个通用的订单处理流程,并通过模板方法模式将具体步骤的实现细节留给了子类。这允许为不同类型的订单创建不同的处理器,而无需改变整个处理流程的结构。

现在可以继续完善这个系统,添加更多的订单处理器类来处理其他特殊类型的订单,例如批量订单或预售订单。这些新的订单处理器类将继承自 OrderProcessor 基类,并实现自己的验证、支付、库存更新和发送邮件的逻辑。

此外,还可以考虑添加错误处理逻辑来增强系统的健壮性。例如,如果在某个步骤中发生错误,可以抛出一个异常,并在调用 processOrder 的地方捕获这个异常,然后采取适当的措施,如记录错误日志或通知用户。

4 模板方法模式的优点与缺点

C++ 模板方法模式的优点主要包括:

(1)代码复用: 模板方法模式允许在父类中定义算法的框架,而将一些步骤的实现延迟到子类中。这样,子类可以复用父类中定义的算法结构,只需要实现特定的步骤即可。这大大减少了重复代码,提高了代码复用性。

(2)灵活性: 通过继承并覆写父类中的特定方法,子类可以定制算法中的某些步骤,从而在不改变算法整体结构的情况下实现不同的行为。这使得算法更加灵活,能够适应多种不同的场景。

(3)扩展性: 当需要添加新的算法步骤或修改现有步骤时,只需要在父类或子类中进行相应的修改,而不需要改动使用算法的代码。这降低了维护成本,提高了系统的可扩展性。

(4)封装性: 模板方法模式将算法的实现细节封装在父类和子类中,客户端代码只需要调用模板方法即可,无需关心算法的具体实现。这提高了代码的封装性,降低了客户端代码与算法实现之间的耦合度。

然而,C++ 模板方法模式也存在一些缺点:

(1)继承的局限性: 模板方法模式依赖于继承机制来实现算法的复用和扩展。然而,继承可能会导致类层次结构过于复杂,增加代码的维护难度。此外,如果一个类已经继承了其他类,那么它可能无法再继承模板方法模式的父类,从而限制了该模式的应用范围。

(2)可能违反开闭原则: 虽然模板方法模式在一定程度上支持开闭原则(对扩展开放,对修改封闭),但在某些情况下,当需要添加新的算法步骤时,可能需要对父类或子类进行修改,从而违反了开闭原则。

(3)难以处理不同算法的差异: 当不同的算法在结构上有很大差异时,使用模板方法模式可能会变得困难。因为模板方法模式要求所有算法都遵循相同的结构,这可能会限制算法的灵活性。

(4)性能考虑: 由于模板方法模式通常涉及到虚函数的调用,这可能会引入一定的性能开销。在性能要求较高的场景中,需要谨慎考虑是否使用模板方法模式。

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

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

相关文章

MySQl基础入门⑦

上一章知识内容 分析数据且区分数据类型 看下表分析数据的特征&#xff0c;根据其特征确定相应的数据类型。 分析以上表格特征&#xff0c;确定数据类型&#xff0c;并对数据进行分类。分析数据后按固定长度字符串、可变长度字符串、整数、固定精度小数和日期时间数据类型对数…

【树】-Lc101-对称二叉树(一棵树是否是另一棵树的子树的变形)

写在前面 最近想复习一下数据结构与算法相关的内容&#xff0c;找一些题来做一做。如有更好思路&#xff0c;欢迎指正。 目录 写在前面一、场景描述二、具体步骤1.环境说明2.代码 写在后面 一、场景描述 对称二叉树。给给定一个二叉树&#xff0c;检查它是否是镜像对称的。 例…

spring-data-elasticsearch官方文档解读(部分)

Spring Data Elasticsearch 这里主要学习的是4.4.16版本的文档 1. 版本 下表显示了 Spring Data 发行版系列使用的 Elasticsearch 版本和其中包含的 Spring Data Elasticsearch 版本&#xff0c;以及引用该特定 Spring Data 发行版系列的 Spring Boot 版本。给出的 Elastics…

keepalived原理以及lvs、nginx跟keeplived的运用

keepalived基础 keepalived的原理是根据vrrp协议&#xff08;主备模式&#xff09;去设定的 vrrp技术相关原理 状态机&#xff1b; 优先级0~255 心跳线1秒 vrrp工作模式 双主双备模式 VRRP负载分担过程 vrrp安全认证&#xff1a;使用共享密匙 keepalived工具介绍 keepal…

Qt 绘制中的视口(setViewport)和窗口(setWindow)

重点 &#xff1a; 1.绘制&#xff08;QPainter&#xff09;可以设置视口&#xff0c;视口下设置窗口&#xff0c;而绘制的构件是以窗口为坐标系进行绘画。 2.先根据绘图设备的物理坐标系的矩形位置&#xff0c;设置视图视口setViewport&#xff0c;然后在以视口为区域去设置…

【Leetcode刷题】1360. 日期之间隔几天

1360. 日期之间隔几天 简单 请你编写一个程序来计算两个日期之间隔了多少天。 日期以字符串形式给出&#xff0c;格式为 YYYY-MM-DD&#xff0c;如示例所示。 示例 1&#xff1a; 输入&#xff1a;date1 “2019-06-29”, date2 “2019-06-30” 输出&#xff1a;1 示例 2…

博客杂谈---程序员如何选择职业赛道?

程序员的职业赛道就像是一座迷宫&#xff0c;有前端的美丽花园&#xff0c;后端的黑暗洞穴&#xff0c;还有数据科学的神秘密室。你准备好探索这个充满挑战和机遇的迷宫了吗&#xff1f;快来了解如何选择职业赛道吧&#xff01; &#xff08;1&#xff09;考虑因素 话题虽然指…

Web题记

[CISCN 2019华北Day2]Web1 告诉我们想要的东西在flag表和flag字段&#xff0c;那应该是sql注入&#xff0c;先试试 试了一些发现会被检查到&#xff0c;随便传数字 除了1和2有返回结果&#xff0c;其余的都报错&#xff0c;应该是数字型注入&#xff0c;抓包看看过滤了哪些 这个…

在DeepLn环境中安装VLLM与ChatGLM3

DeepLn | 智慧算力触手可及是一个挺便宜的算力租用平台&#xff0c;里面有大量的显卡可以租用。唯一美中不足的是&#xff0c;提供的pytorch版本低&#xff0c;只支持到2.01&#xff0c;为了匹配vllm&#xff0c;需要手动安装指定版本的pytorch。 vllm介绍 总体而言&#xff0…

如何关闭vscode灰色代码提示

vscode编辑rust代码时&#xff0c;rust-analyze插件会默认给代码添加一些提示&#xff08;灰色代码&#xff09;&#xff0c;这部分代码 不会保存到文件&#xff0c;仅仅是为了你方便看各种变量的定义等。 但有时会觉得代码很乱&#xff0c;所以按以下方法可以把他隐藏。 1&am…

接收端编程、UDP编程练习、wireshrak抓包工具、UDP包头

我要成为嵌入式高手之3月6日Linux高编第十六天&#xff01;&#xff01; ———————————————————————————— 学习笔记 接收端 recvfrom #include <sys/types.h> #include <sys/socket.h> ssize_t recvfrom(int sockfd, void *buf, si…

JVM(Java虚拟机)概述

1. JVM的定义和作用 JVM&#xff08;Java Virtual Machine&#xff09;是一个能够运行Java字节码的虚拟计算机。它是Java平台的核心组成部分&#xff0c;负责执行编译后的Java程序&#xff0c;提供跨平台运行的能力。JVM使得Java程序可以在任何安装了JVM的操作系统上运行&#…

【50天50个项目】旋转导航页面

实现效果&#xff1a; HTML <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><link rel"stylesheet&…

asp飞机订票-57-(说明+代码)

演示查看 http://pc.3q2008.Com/3q2008_Com/hkdp 目 录 1&#xff0e; 系统规划 3 1&#xff0e;1 行业背景 3 1&#xff0e;2 行业现状 3 1.2.1用户注册 3 1.2.2航班查询 3 1.2.3在线订票 3 1.2.4在线支付 3 1.2.5电子客票 4 1.2.6其它辅助产品与服务 4 1&#xff0e;3 需求…

[Mac软件]Adobe Illustrator 2024 28.3 intel/M1/M2/M3矢量图制作软件

应用介绍 Adobe Illustrator 是行业标准的矢量图形应用程序&#xff0c;可以为印刷、网络、视频和移动设备创建logos、图标、绘图、排版和插图。数以百万计的设计师和艺术家使用Illustrator CC创作&#xff0c;从网页图标和产品包装到书籍插图和广告牌。 绘制任意大小的标志 拥…

Timus#1005

C【动态规划】 #include<iostream> #include<vector> using namespace std; int main() {int n;cin >> n;vector<int> dp(100000 * 20);vector<int> a(n);int ans 0, cur 0;for (int i 0; i < n; i){cin >> a[i];ans a[i];}int sum…

C++ 求不同数组的最大值

编写一个名为max()的函数&#xff0c;用于求包含n个元素的数组的最大值&#xff0c;要求通过函数重载能求int、double、char、string的最大值。 函数接口定义&#xff1a; int max(int *a, int n) ; double max(double *a, int n) ; char max(char *a, int n); string max(str…

探索直播美颜SDK背后的深度学习算法:智能化美肤与特效实现

美颜SDK背后的技术原理和深度学习算法近期很多读者向小编提问。今天&#xff0c;我将为大家深入讲解直播美颜SDK背后的深度学习算法&#xff0c;以及智能化美肤与特效实现的原理与应用。 一、美颜SDK的背后&#xff1a;深度学习算法 美颜SDK是一种集成了多种美颜功能的软件开…

【APB协议 UVM_Sequencer Driver Monitor_2024.03.04】

apb协议 写时序 地址、写信号、PSEL、写数据信号同时发生变化&#xff0c;即传输的第一个时钟被称为SETUP周期。在下个时钟上升沿,PENABLE信号拉高&#xff0c;表示ENABLE周期&#xff0c;在该周期内&#xff0c;数据、地址以及控制信号都必须保持有效。整个写传输在这个周期…

分享几个Google Chrome谷歌浏览器历史版本下载网站

使用selenium模块的时候&#xff0c;从官网下载的谷歌浏览器版本太高&#xff0c;驱动不支持&#xff0c;所以需要使用历史的谷歌浏览器版本 &#xff0c;这里备份一下以防找不到了。 驱动下载地址&#xff1a;https://registry.npmmirror.com/binary.html?pathchromedriver 文…