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

设计模式 19 模板模式 Template Pattern

设计模式 19 模板模式 Template Pattern

1.定义

        模板模式(Template Pattern)是一种行为设计模式,它定义了一个算法的骨架,将一些步骤的具体实现延迟到子类中。在模板模式中,定义了一个抽象类,其中包含了一个模板方法(template method),这个方法定义了算法的基本结构和步骤,但其中的具体步骤由子类来实现。

        模板模式主要用于在不同子类中封装通用的行为,同时保持整体算法结构的一致性。通过模板方法模式,使得父类能够控制方法的执行顺序,同时细节延迟到子类实现。

2.内涵

模板模式包含以下几个角色:

  1. Abstract Class(抽象类):定义了一个模板方法,该方法是算法的骨架,其中调用了一系列抽象或具体的步骤。抽象类可能还包含了一些通用的实现,这些实现可以在模板方法中直接调用,也可以在需要时在子类中覆盖。
  2. Concrete Class(具体类):继承自抽象类,并实现了其中的具体步骤。每个具体类可以根据需要实现抽象类中定义的方法,并将其组合在一起形成完整的算法。

通过模板模式,可以实现代码复用、减少重复代码的编写,同时保持算法的统一性和一致性。模板模式能够提供一个稳定的算法框架,同时允许子类自由扩展或修改算法的某些部分。

=======================
|      AbstractClass   |
|----------------------|
|+ templateMethod()   |  
|+ primitiveOperation1()|
|+ primitiveOperation2()|
=======================||||=====================|    ConcreteClassA   ||----------------------||# primitiveOperation1()||# primitiveOperation2()|=====================||||=====================|   ConcreteClassB    ||----------------------||# primitiveOperation1()||# primitiveOperation2()|=====================


     
AbstractClass(抽象类):这是模板模式的核心部分,抽象类定义了模板方法 templateMethod() 和两个抽象方法 primitiveOperation1() 和 primitiveOperation2(),其中 templateMethod() 指定了算法的框架,包括一系列调用步骤,而 primitiveOperation1() 和 primitiveOperation2() 则是需要在具体子类中实现的具体步骤。

ConcreteClassA 和 ConcreteClassB(具体类):这是实际实现模板模式的具体子类。它们继承了 AbstractClass 并实现了其中的抽象方法 primitiveOperation1() 和 primitiveOperation2()。每个具体类可能实现完全不同的具体步骤,但是它们遵循相同的模板方法结构(templateMethod())。

     
     
3.使用示例

/*** The Abstract Class defines a template method */
class AbstractClass {/*** The template method defines the skeleton of an algorithm.*/public:void TemplateMethod() const {this->BaseOperation1();this->RequiredOperations1();this->BaseOperation2();this->Hook1();this->RequiredOperation2();this->BaseOperation3();this->Hook2();}protected:void BaseOperation1() const {std::cout << "AbstractClass says: I am doing the bulk of the work\n";}void BaseOperation2() const {std::cout << "AbstractClass says: But I let subclasses override some operations\n";}void BaseOperation3() const {std::cout << "AbstractClass says: But I am doing the bulk of the work anyway\n";}virtual void RequiredOperations1() const = 0;virtual void RequiredOperation2() const = 0;virtual void Hook1() const {}virtual void Hook2() const {}
};/*** Concrete classes have to implement all abstract operations of the base class.* They can also override some operations with a default implementation.*/
class ConcreteClass1 : public AbstractClass {protected:void RequiredOperations1() const override {std::cout << "ConcreteClass1 says: Implemented Operation1\n";}void RequiredOperation2() const override {std::cout << "ConcreteClass1 says: Implemented Operation2\n";}
};class ConcreteClass2 : public AbstractClass {protected:void RequiredOperations1() const override {std::cout << "ConcreteClass2 says: Implemented Operation1\n";}void RequiredOperation2() const override {std::cout << "ConcreteClass2 says: Implemented Operation2\n";}void Hook1() const override {std::cout << "ConcreteClass2 says: Overridden Hook1\n";}
};void ClientCode(AbstractClass *class_) {// ...class_->TemplateMethod();// ...
}int main() {std::cout << "Same client code can work with different subclasses:\n";ConcreteClass1 *concreteClass1 = new ConcreteClass1;ClientCode(concreteClass1);std::cout << "\n";std::cout << "Same client code can work with different subclasses:\n";ConcreteClass2 *concreteClass2 = new ConcreteClass2;ClientCode(concreteClass2);delete concreteClass1;delete concreteClass2;return 0;
}


输出:

Same client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass1 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass1 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anywaySame client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass2 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass2 says: Overridden Hook1
ConcreteClass2 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anyway

上述代码类图


4.注意事项

在使用模板模式(Template Pattern)时,有一些注意事项需要注意以避免可能的问题和踩坑:

  • 抽象类的设计:在定义抽象类时,需要仔细考虑模板方法中的逻辑和步骤顺序,确保所有子类都能正确实现这些步骤。避免在抽象类中定义过多的具体实现,应保持抽象类的简洁性,只包含框架结构和必要的抽象方法。
  • 子类实现的一致性:子类在实现抽象方法时,需要保持接口一致性,确保方法签名和返回类型与父类中定义的一致。否则可能会导致编译错误或运行时异常。
  • 算法的扩展性:模板模式允许子类修改或扩展算法的某些部分,但注意避免过度扩展和修改,以免破坏算法的一致性和规范性。确保扩展的功能是相对独立且有必要的。
  • 模板方法的执行顺序:模板方法定义了算法的执行顺序,子类在实现具体步骤时要遵循这个顺序。任何不当的调整可能会导致算法步骤的混乱或错误。
  • 继承的关系:模板模式使用了继承机制,但应该避免过度使用继承导致类层次结构复杂。确保继承关系的合适性和合理性。
  • 钩子方法:在抽象类中可以定义钩子方法,用于控制算法的某些部分是否执行。需要谨慎设计和使用钩子方法,避免影响算法的整体结构和正确性。
  • 单一职责原则:确保每个具体类只负责实现自己的具体步骤,遵循单一职责原则,避免一个类承担过多的责任和功能。
5.最佳实践


在开发中实现模板模式(Template Pattern)时,可以采用以下一些比较好的经验:

  • 合理抽象和分离关注点:在设计抽象类时,需要将通用的算法步骤抽象出来,同时将具体步骤留给具体子类实现。这样可以实现算法的复用和解耦,同时方便后续的扩展和修改。
  • 使用钩子方法:钩子方法是一种可以控制算法流程的手段,可以在抽象类中定义一些空方法或默认实现,具体子类可以选择性地重写这些方法来影响算法的执行。这样可以在不改变模板方法结构的情况下灵活地扩展和定制算法。
  • 保持一致性:确保所有具体子类实现的具体步骤都是与抽象类中定义的一致的,保持算法的一致性和规范性,避免出现错误或混乱。
  • 封装变化部分:在设计模板模式时,将会变化的部分抽象出来,以便随时扩展和修改,而将不变的部分固定在模板方法中,确保算法的稳定性和可维护性。
  • 使用工厂方法:在具体类的实例化时,可以考虑使用工厂方法模式(Factory Method Pattern),将实例化过程放在具体的工厂类中,以便灵活地切换不同的具体子类。

6.总结

通过模板模式,可以实现代码复用、减少重复代码的编写,同时保持算法的统一性和一致性。模板模式能够提供一个稳定的算法框架,同时允许子类自由扩展或修改算法的某些部分。


 

相关文章:

设计模式 19 模板模式 Template Pattern

设计模式 19 模板模式 Template Pattern 1.定义 模板模式&#xff08;Template Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一个算法的骨架&#xff0c;将一些步骤的具体实现延迟到子类中。在模板模式中&#xff0c;定义了一个抽象类&#xff0c;其中包含了一个…...

PHP如何实现实时计算使用者消耗服务器资源费用?

最近几天遇到一个客户,提出一个很有意思的东西!当然客户的项目方案这里不方便说,这里就假定客户的项目是腾讯云?哈哈哈哈哈 以前客户的收费方案是按月、按季度、按年收费,现在半路杀出了很多程咬金,导致之前的收费方案有点儿贵,没啥性价比,那就搞一个看起来很“便宜”…...

在C++中自定义命名空间,在命名空间中定义string变量,同时定义一个函数实现单词逆置

代码 #include <iostream> #include <cstring> using namespace std; namespace my_space {string s;void reverse(string s);//定义逆置函数 } using namespace my_space; void my_space::reverse(string s){int lens.size();int i0;int jlen-1;while(i<j){//…...

【leetcode 141】环形链表——快慢指针(龟兔赛跑)

给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&#xff08;…...

容器(Container)的详细介绍

容器&#xff0c;作为现代软件开发和部署的核心技术之一&#xff0c;已经成为云计算、微服务架构等领域的基石。容器技术通过提供轻量级的虚拟化环境&#xff0c;实现了应用程序的快速部署、迁移和扩展&#xff0c;极大地提高了软件开发的效率和灵活性。本文将详细介绍容器的概…...

Python 网格变换之平移、旋转、缩放、变换矩阵

网格变换 一、平移1.1、代码示例1.2、结果示例二、旋转2.1、代码示例2.2、结果示例三、缩放3.1、代码示例3.2、结果示例四、变换矩阵4.1、代码示例4.2、结果示例一、平移 网格平移:将网格沿着特定的方向移动一段距离。 1.1、代码示例...

推荐10款优秀的组件库(一)

1.Ant Desgin UI 网址&#xff1a; https://ant-design-mobile.antgroup.com/zh Ant Design - 一套企业级 UI 设计语言和 React 组件库 "Ant Design Mobile"是一个在线的移动端Web体验平台&#xff0c;让你探索移动端Web的体验极限。 添加图片注释&#xff0c;不…...

freertos的信号量和互斥锁学习笔记

freertos的信号量和互斥锁有两个比较形象的例子可以解释两者的主要用途。 第一个是信号量&#xff1a; 使用信号量的最初目的是为了给共享 资源建立一个标志&#xff0c;该标志表示该共享资源被占用情况。这样&#xff0c;当一个任务在访问共享资源之前&#xff0c;可以先对这…...

C++基础——vector的详解与运用

vector的介绍 文档介绍 vector是表示可变大小数组的序列容器。就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问&#xff0c;和数组一样高效。但是又不像数组&#xff0c;它的大小是可以动态改变的&#xff0c;…...

const指针,星号判断方法

一 示例代码 1. const char *p // 指向常量的指针 2. char const *p // 指向常量的指针 3. char * const p // 指针常量二 判断方法 const在星号左边&#xff0c;指向常量的指针&#xff0c;指针p可修改。 const在星号右边&#xff0c;指针常量&#xff0c;指针p不可修改。...

移动摄像头专网需要解vlan,如何解决

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…...

5.27周报

这两周邻近毕业故没有很多时间来学习课余内容&#xff0c;另外最近身体有些不舒服【偏头痛】&#xff0c;所以学的内容不多&#xff0c;包括SVM向量机和ResNet【不包括代码复现】 1.SVM支持向量机的大概内容 1、目的&#xff1a; 主要内容是如何找到分类的那条线【超平面】—…...

C-数据结构-树状存储的基本实现

/* 理解和记忆递归的关键在于把握递归的本质和函数调用的过程。递归函数在每次调用时会把当前状态压入调用栈&#xff0c;直到满足终止条件后开始回溯。理解基准条件和递归步骤&#xff1a;每个递归函数都需要有基准条件&#xff08;如节点为空时返回&#xff09;&#xff0c;并…...

指纹识别经典图书、开源算法库、开源数据库

目录 1. 指纹识别书籍 1.1《精通Visual C指纹模式识别系统算法及实现》 1.2《Handbook of Fingerprint Recognition》 2. 指纹识别开源算法库 2.1 Hands on Fingerprint Recognition with OpenCV and Python 2.2 NIST Biometric Image Software (NBIS) 3. 指纹识别开源数…...

嵌入式之译码器

系列文章目录 译码器嵌入式之译码器 嵌入式之译码器 系列文章目录一、译码器定义二、常见类型的译码器三、工作原理 一、译码器定义 译码器&#xff08;Decoder&#xff09;是一种数字电路&#xff0c;其主要功能是从输入的编码信号中解码出特定的信息或控制信号。 译码器通常…...

分成sum接近的2个集合,返回相对小的sum

题目描述&#xff1a;给定一个正数数组arr&#xff0c;请把arr中所有的数分成两个集合&#xff0c;尽量让两个集合的累加和接近&#xff0c;返回最接近的情况下&#xff0c;较小集合的累加和sum。 way&#xff1a;选还是不选 //arr[index...]可以自由选择,返回累加和尽量接近…...

SpringBoot前置知识01-SPI接口

SpringBoot前置知识-SPI接口 介绍 Java中SPI是一种服务发现机制&#xff0c;或者说是一种思想&#xff0c;亦是一种约定。其实JDK中的JDBC就是使用了这种用思想&#xff0c;JDBC在JDK中只定义了接口&#xff0c;并没有实现类&#xff0c;连接什么数据库就要引入什么数据库的驱…...

数学建模--LaTeX的基本使用

目录 1.回顾 2.设置这个页眉和页脚 3.对于字体的相关设置 4.对于这个分级标题的设置 5.列表的使用 6.插入图片 1.回顾 &#xff08;1&#xff09;昨天我们了解到了这个latex的使用基本常识&#xff0c;以及这个宏包的概念&#xff0c;区域的划分&#xff0c;不同的代码代…...

授权调用: 介绍 Transformers 智能体 2.0

简要概述 我们推出了 Transformers 智能体 2.0&#xff01; ⇒ &#x1f381; 在现有智能体类型的基础上&#xff0c;我们新增了两种能够 根据历史观察解决复杂任务的智能体。 ⇒ &#x1f4a1; 我们致力于让代码 清晰、模块化&#xff0c;并确保最终提示和工具等通用属性透明化…...

流媒体内网穿透/组网/视频协议转换EasyNTS上云网关如何更改密码?

EasyNTS上云网关的主要作用是解决异地视频共享/组网/上云的需求&#xff0c;网页对域名进行添加映射时&#xff0c;添加成功后会生成一个外网访问地址&#xff0c;在浏览器中输入外网访问地址&#xff0c;即可查看内网应用。无需开放端口&#xff0c;EasyNTS上云网关平台会向Ea…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南

1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;使用DevEco Studio作为开发工具&#xff0c;采用Java语言实现&#xff0c;包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件&#xff0c;用于在原生应用中加载 HTML 页面&#xff1a; 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

IP如何挑?2025年海外专线IP如何购买?

你花了时间和预算买了IP&#xff0c;结果IP质量不佳&#xff0c;项目效率低下不说&#xff0c;还可能带来莫名的网络问题&#xff0c;是不是太闹心了&#xff1f;尤其是在面对海外专线IP时&#xff0c;到底怎么才能买到适合自己的呢&#xff1f;所以&#xff0c;挑IP绝对是个技…...