C++实现设计模式---模板方法模式 (Template Method)

server/2025/1/15 20:08:46/

模板方法模式 (Template Method)

模板方法模式 是一种行为型设计模式,它定义了一个操作中的算法骨架,将某些步骤的实现延迟到子类。通过模板方法,子类可以在不改变算法结构的情况下重新定义算法的某些步骤。


意图

  • 在一个方法中定义算法的骨架,而将某些具体步骤延迟到子类中实现。
  • 让子类可以在不改变算法整体结构的情况下重新定义算法的某些步骤。

使用场景

  1. 多个类有相同的操作步骤,但具体实现不同
    • 当多个类的操作有一致的结构,但实现不同,可以使用模板方法模式
  2. 希望控制算法的结构
    • 父类定义算法骨架,子类实现具体步骤。
  3. 避免代码重复
    • 将公共逻辑抽取到父类中,具体逻辑在子类中实现。

参与者角色

  1. 抽象类 (AbstractClass)
    • 定义算法的骨架,并包含一个或多个抽象方法,子类需要实现这些方法。
    • 提供一个模板方法,定义算法的整体结构。
  2. 具体类 (ConcreteClass)
    • 实现抽象类中的抽象方法,提供算法的具体步骤。

示例代码

以下代码展示了模板方法模式的实现,用于模拟制作饮品的过程。不同的饮品(如茶和咖啡)有相同的制作步骤,但某些步骤的实现不同。

#include <iostream>// 抽象类:饮品
class Beverage {
public:virtual ~Beverage() = default;// 模板方法:定义饮品制作的算法骨架void prepareRecipe() {boilWater();           // 1. 煮沸水brew();                // 2. 冲泡pourInCup();           // 3. 倒入杯中if (customerWantsCondiments()) { // 4. 是否添加调料addCondiments();   // 添加调料}}protected:// 基本操作void boilWater() {std::cout << "将水煮沸。
";}void pourInCup() {std::cout << "将饮品倒入杯中。
";}// 抽象操作:由子类实现virtual void brew() = 0;virtual void addCondiments() = 0;// Hook(钩子方法):子类可选择重写virtual bool customerWantsCondiments() {return true; // 默认需要添加调料}
};// 具体类:茶
class Tea : public Beverage {
protected:void brew() override {std::cout << "用热水浸泡茶叶。
";}void addCondiments() override {std::cout << "添加柠檬。
";}
};// 具体类:咖啡
class Coffee : public Beverage {
protected:void brew() override {std::cout << "用热水冲泡咖啡。
";}void addCondiments() override {std::cout << "添加牛奶和糖。
";}// 重写钩子方法bool customerWantsCondiments() override {char answer;std::cout << "是否需要添加牛奶和糖?(y/n): ";std::cin >> answer;return (answer == 'y' || answer == 'Y');}
};// 客户端代码
int main() {std::cout << "制作茶:
";Tea tea;tea.prepareRecipe();std::cout << "
制作咖啡:
";Coffee coffee;coffee.prepareRecipe();return 0;
}

代码解析

1. 抽象类 (Beverage)
  • 定义了模板方法 prepareRecipe,它包含制作饮品的算法骨架。
  • 提供了基本操作(如 boilWaterpourInCup),以及抽象操作(如 brewaddCondiments),具体实现由子类完成。
  • 包含钩子方法 customerWantsCondiments,允许子类自定义是否执行某些步骤。
class Beverage {
public:virtual ~Beverage() = default;void prepareRecipe() {boilWater();brew();pourInCup();if (customerWantsCondiments()) {addCondiments();}}protected:void boilWater() { std::cout << "将水煮沸。
"; }void pourInCup() { std::cout << "将饮品倒入杯中。
"; }virtual void brew() = 0;virtual void addCondiments() = 0;virtual bool customerWantsCondiments() { return true; }
};
2. 具体类 (Tea, Coffee)
  • Tea
    • 实现了 brewaddCondiments 方法,定义了茶的制作方式。
  • Coffee
    • 实现了 brewaddCondiments 方法,定义了咖啡的制作方式。
    • 重写了 customerWantsCondiments 方法,允许用户选择是否添加调料。
class Tea : public Beverage {
protected:void brew() override { std::cout << "用热水浸泡茶叶。
"; }void addCondiments() override { std::cout << "添加柠檬。
"; }
};
3. 客户端代码
  • 客户端通过调用模板方法 prepareRecipe 来制作饮品,具体实现由子类决定。
int main() {Tea tea;tea.prepareRecipe();Coffee coffee;coffee.prepareRecipe();
}

优缺点

优点
  1. 复用代码
    • 将公共逻辑抽取到模板方法中,减少代码重复。
  2. 灵活性
    • 子类可以通过实现抽象方法或重写钩子方法来自定义算法的某些步骤。
  3. 控制算法结构
    • 父类定义算法结构,保证子类不会破坏算法整体逻辑。
缺点
  1. 对子类的依赖
    • 子类需要实现抽象方法,可能导致类的数量增加。
  2. 模板方法难以扩展
    • 如果模板方法需要修改,可能会影响所有子类。

适用场景

  1. 多个子类有相同的算法结构,但实现不同
    • 如制作不同饮品的过程。
  2. 希望控制算法的执行流程
    • 父类定义算法骨架,子类实现具体步骤。
  3. 需要扩展算法的某些步骤
    • 通过子类实现具体步骤,保证算法结构不变。

总结

模板方法模式通过定义算法的骨架,将具体实现延迟到子类中,从而实现代码复用和灵活扩展。它特别适用于算法结构固定但某些步骤实现不同的场景。


http://www.ppmy.cn/server/158645.html

相关文章

力扣264. 丑数 II

给你一个整数 n &#xff0c;请你找出并返回第 n 个 丑数 。丑数 就是质因子只包含 2、3 和 5 的正整数。 //用一个数组来保存第1到第n个丑数 //一个丑数必须是乘以较小的丑数的 2、3 或 5来得到。 //使用三路合并方法&#xff1a;L2、L3 和 L5三个指针遍历2、3、5倍的丑数序列…

C++例程:使用I/O模拟IIC接口(6)

完整的STM32F405代码工程I2C驱动源代码跟踪 一&#xff09;myiic.c #include "myiic.h" #include "delay.h" #include "stm32f4xx_rcc.h" //初始化IIC void IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphCl…

MC1.12.2 macOS高清修复OptiFine运行崩溃

最近在玩RLCraft&#xff0c;在windows中运行正常的&#xff0c;移植到macOS中发现如果加载OptiFine模组就会崩溃 报错日志 报错日志如下&#xff0c;其中已经包含了各种版本信息&#xff0c;我就不单独说明了。这里说一下&#xff0c;报错的时候用的是oracle jdk x64的&…

测试开发之面试宝典

目录 session和cookie的区别 session和cookie的区别 1.session和cookie都是鍵值对应的 2.session和cookie都是服务器生成的&#xff0c;session的ID&#xff0c;即服各器用来识别读取session对象的一把钥匙 3.session是保存在服各器端&#xff0c;而cookie是返回給客戶端的&…

了解如何学习自然语言处理技术

可以从以下几个方面入手&#xff1a; 1. 掌握基础理论知识 数学基础&#xff1a; 概率论与数理统计&#xff1a;NLP 很多算法基于概率模型&#xff0c;如语言模型用于预测一个词序列出现的概率&#xff0c;朴素贝叶斯分类器在文本分类中广泛应用&#xff0c;这些都依赖概率计算…

Docker 安装开源的IT资产管理系统Snipe-IT

一、安装 1、创建docker-compose.yaml version: 3services:snipeit:container_name: snipeitimage: snipe/snipe-it:v6.1.2restart: alwaysports:- "8000:80"volumes:- ./logs:/var/www/html/storage/logsdepends_on:- mysqlenv_file:- .env.dockernetworks:- snip…

Golang——GPM调度器

本文详细介绍Golang的GPM调度器&#xff0c;包括底层源码及其实现&#xff0c;以及一些相关的补充知识。 文章目录 前情提要并发与并行并行 (Parallel)并发 (Concurrency)关键区别 进程和线程的区别协程解决的问题协程的优势 Go的并发模型-CSPGo的调度模型-GPM源码Goroutineg 结…

什么叫慢查询 ?什么情况下出现?怎么解决,怎么优化 在微服务中

慢查询定义 慢查询&#xff08;Slow Query&#xff09;是指在数据库系统中执行时间超过预设阈值的SQL或NoSQL查询。这些查询通常会消耗较多的资源&#xff0c;可能导致数据库性能下降&#xff0c;并且可能影响到其他正在运行的查询或事务。 慢查询出现的情况 慢查询可以在多…