【设计模式】 模板方法模式介绍及C代码实现

news2024/9/28 23:33:50

【设计模式】 模板方法模式介绍及C代码实现

背景

  在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现。
  比如你要从北京去上海出差,出差的工作是不变的,但是使用的交通工具却有不同的方式,可能有火车、可能飞机、可能开车。如果写程序实现,则每次都要写一个不同的类,并且类中实现功能几乎一样,大量重复的逻辑,相信你已经闻到这里面的一些坏味道了。 这种整体功能稳定,但是子步骤经常改变的需求,就可以使用模板方法设计模式来优化。

定义

那什么是模板方法设计模式?

  模板方法设计模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。这样就使得子类可以不改变一个算法的结构即可重定义(override 重写)该算法的某些特定步骤。

  模板方法模式的主要思想是基于“好莱坞原则”,即“不要打电话给我们,我们会打电话给你”。这意味着在模板方法模式中,父类定义了一个算法框架,但是具体的实现由子类决定。子类可以通过继承父类,并重写父类的某些方法来实现自己的具体实现。

  模板方法模式通常由两个部分组成:抽象父类和具体子类。抽象父类定义了一个算法框架,其中包含了一些抽象方法和具体方法。抽象方法由子类实现,具体方法由父类实现。具体子类通过重写抽象方法来实现自己的具体实现,从而完成整个算法。

应用场景

模板方法设计模式主要应用在以下场景:

  • 当你只希望客户端扩展某个特定算法步骤, 而不是整个算法或其结构时, 可使用模板方法模式。模板方法将整个算法转换为一系列独立的步骤, 以便子类能对其进行扩展, 同时还可让超类中所定义的结构保持完整。

  • 当多个类的算法除一些细微不同之外几乎完全一样时,可以使用该模式。 但其后果就是, 只要算法发生变化, 你就可能需要修改所有的类。在将算法转换为模板方法时, 你可将相似的实现步骤提取到超类中以去除重复代码。 子类间各不同的代码可继续保留在子类中。

模式结构

在这里插入图片描述

抽象类 (Abstract­Class) 会声明作为算法步骤的方法, 以及依次调用它们的实际模板方法。 算法步骤可以被声明为 抽象类型, 也可以提供一些默认实现。

具体类 (Concrete­Class) 可以重写所有步骤, 但不能重写模板方法自身。

实现步骤

模板方法设的主要实现步骤:

  1. 分析目标算法, 确定能否将其分解为多个步骤。 从所有子类的角度出发, 考虑哪些步骤能够通用, 哪些步骤各不相同。

  2. 创建抽象基类并声明一个模板方法和代表算法步骤的一系列抽象方法。 在模板方法中根据算法结构依次调用相应步骤。 可用 final最终修饰模板方法以防止子类对其进行重写。

  3. 虽然可将所有步骤全都设为抽象类型, 但默认实现可能会给部分步骤带来好处, 因为子类无需实现那些方法。

  4. 可考虑在算法的关键步骤之间添加钩子。

  5. 为每个算法变体新建一个具体子类, 它必须实现所有的抽象步骤, 也可以重写部分可选步骤。

C语言代码示例

  其实设计模式是一种与编程语言无关的设计思想,但是其中很重要的思想就是面向对象,所以在面向对象的语言,比如C++、Java中实现起来就非常顺手,但因为我本人是C语言出身,并且作为主要编程语言的,所以就使用了C语言来实现模板方法的设计模式。

在C语言中,我们可以通过函数指针和结构体来实现模板模式。下面是一个示例:

#include <stdio.h>

// 抽象类,定义算法框架
typedef struct {
    void (*step1)(void);
    void (*step2)(void);
    void (*step3)(void);
    void (*run)(void);
} Algorithm;

// 具体子类,实现具体步骤
typedef struct {
    Algorithm super;
    int data;
} MyAlgorithm;

void my_algorithm_step1(void) {
    MyAlgorithm* my_algorithm = (MyAlgorithm*)Algorithm_GetThis();
    printf("MyAlgorithm Step 1 with data %d\n", my_algorithm->data);
}

void my_algorithm_step2(void) {
    MyAlgorithm* my_algorithm = (MyAlgorithm*)Algorithm_GetThis();
    printf("MyAlgorithm Step 2 with data %d\n", my_algorithm->data);
}

void my_algorithm_step3(void) {
    MyAlgorithm* my_algorithm = (MyAlgorithm*)Algorithm_GetThis();
    printf("MyAlgorithm Step 3 with data %d\n", my_algorithm->data);
}

void my_algorithm_run(void) {
    Algorithm* algorithm = Algorithm_GetThis();
    algorithm->step1();
    algorithm->step2();
    algorithm->step3();
}

// 定义抽象类的构造函数
Algorithm* Algorithm_Create(void) {
    Algorithm* algorithm = (Algorithm*)malloc(sizeof(Algorithm));
    algorithm->step1 = NULL;
    algorithm->step2 = NULL;
    algorithm->step3 = NULL;
    algorithm->run = NULL;
    return algorithm;
}

// 定义具体子类的构造函数
MyAlgorithm* MyAlgorithm_Create(int data) {
    MyAlgorithm* my_algorithm = (MyAlgorithm*)malloc(sizeof(MyAlgorithm));
    my_algorithm->super.step1 = my_algorithm_step1;
    my_algorithm->super.step2 = my_algorithm_step2;
    my_algorithm->super.step3 = my_algorithm_step3;
    my_algorithm->super.run = my_algorithm_run;
    my_algorithm->data = data;
    return my_algorithm;
}

// 定义获取this指针的函数,用于将函数指针转换为正确的类型
Algorithm* Algorithm_GetThis(void) {
    // 这里假设调用者已经将this指针保存在全局变量中
    // 实际使用时应该根据具体情况修改这个函数的实现
    return (Algorithm*)this;
}

int main() {
    // 创建一个MyAlgorithm的实例
    MyAlgorithm* my_algorithm = MyAlgorithm_Create(42);

    // 调用run函数运行算法
    this = (void*)my_algorithm; // 将this指针保存在全局变量中
    my_algorithm->super.run();

    // 释放资源
    free(my_algorithm);

    return 0;
}

  在这个例子中,我们定义了一个抽象类 Algorithm,它包含了算法的框架。step1、step2、step3和run这些成员变量都是函数指针,用来定义算法的具体步骤。

  我们还定义了一个具体子类 MyAlgorithm,它继承了 Algorithm,并实现了具体的步骤。这里我们添加了一个 data 成员变量,用于在每个步骤中输出一些信息。

  在具体子类的构造函数 MyAlgorithm_Create 中,我们将 MyAlgorithm 的各个成员变量初始化为具体的函数实现。在 run 函数中,我们按照 Algorithm 的算法框架依次调用各个步骤。

  最后,在 main 函数中,我们创建了一个 MyAlgorithm 的实例,并调用了它的 run 函数运行算法。注意到我们使用了一个全局变量 this 来保存当前的 this 指针,用于在每个步骤中获取 MyAlgorithm 实例的成员变量。实际使用时,我们应该根据具体情况修改这个实现。

  可以说模板方法模式是在开发过程中还是非常常见并且有用的,它可以让我们轻松地定义算法框架,并将具体的实现延迟到子类中。在 C 语言中,我们主要可以使用函数指针和结构体来实现模板模式。

总结

  模板方法模式的优点在于可以封装算法的骨架,让子类专注于具体实现细节。这样可以使得代码具有更好的可维护性、可读性和可扩展性。此外,模板方法模式还能够在不改变算法框架的情况下,扩展算法的功能,从而满足不同的业务需求。

  比如你可以只重写工程中一个大型算法中的特定部分,而不修改其他部分,使得算法其他部分修改对其所造成的影响减小。也可以将重复代码提取到一个超类中。

  总之,模板方法模式是一种非常实用的设计模式,它可以让我们轻松地定义算法框架,并将具体的实现延迟到子类中。这种模式可以提高代码的重用性和可维护性,是面向对象编程中必不可少的一种设计思想。

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

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

相关文章

2023年1月综合预订类APP用户洞察——旅游市场复苏明显,三年需求春节集中释放

2023年1月&#xff0c;随着国家对新型冠状病毒感染实施“乙类乙管”&#xff0c;不再对入境人员和货物等采取检疫传染病管理措施&#xff0c;并且取消入境后全员核酸检测和集中隔离&#xff0c;横亘在旅游者与旅游目的地之间的隔阂从此彻底消失。2023年1月恰逢春节假期&#xf…

SQL零基础入门学习(十一)

SQL零基础入门学习&#xff08;十&#xff09; SQL NOT NULL 约束 NOT NULL 约束强制列不接受 NULL 值。 NOT NULL 约束强制字段始终包含值。这意味着&#xff0c;如果不向字段添加值&#xff0c;就无法插入新记录或者更新记录。 下面的 SQL 强制 “ID” 列、 “LastName” …

Mac OSX下使用VMware Fusion 配置静态IP 图文教程指南

目录一. 前言二. Mac OSX下使用VMware Fusion 配置静态IP2.1 了解静态IP如何划分基础知识2.2 Centos7 安装操作系统时图形界面配置静态IP2.3 Centos7安装操作系统后修改动态IP为静态IP三参考文献一. 前言 Mac OSX 下使用VMware Fusion 创建的虚拟机&#xff0c;默认是通过DHCP…

雷达实战之射频前端配置说明

在无线通信领域&#xff0c;射频系统主要分为射频前端,以及基带。从发射通路来看&#xff0c;基带完成语音等原始信息通过AD转化等手段转化成基带信号&#xff0c;然后经过调制生成包含跟多有效信息&#xff0c;且适合信道传输的信号&#xff0c;最后通过射频前端将信号发射出去…

msys2+minGW方案编译ffmpeg的最佳实践

一、Win10 64bit编译环境的建立1&#xff09;从http://www.msys2.org/下载 msys2-x86_64-xxx.exe2&#xff09; 安装msys2到默认路径 C:\msys64\3&#xff09; 运行MSYS2 w644&#xff09;执行 pacman -Syu 更新系统当出现提示时&#xff0c;选择y5) 当窗口关闭时&#xff0c;重…

九龙证券|美股创年内最大周跌幅!美联储官员密集发声!波音重挫近5%

当地时刻2月24日&#xff0c;美股三大指数收盘明显跌落。道指跌1.02%&#xff0c;标普500指数跌1.05%&#xff0c;纳指跌1.69%。 大型科技股普跌&#xff0c;微软、亚马逊跌超2%。波音大跌4.8%&#xff0c;居道指跌幅榜首位&#xff0c;公司因机身部件有问题再次暂停向用户交付…

zabbix4.0-动作-邮件告警

目录 1、创建动作Actions 动作触发流程 创建一个动作 2、配置 Media types 媒介类型&#xff0c;添加一个发件邮箱来发送告警邮件 3、配置 Users Media&#xff0c;添加一个收件邮箱来接收告警邮件 4、更改一个触发器表达式来触发动作Action&#xff0c;最终发送告警邮…

【数据库】MongoDB数据库详解

目录 一&#xff0c;数据库管理系统 1&#xff0c; 什么是数据库 2&#xff0c;什么是数据库管理系统 二&#xff0c; NoSQL 是什么 1&#xff0c;NoSQL 简介 2&#xff0c;NoSQL数据库 3&#xff0c;NoSQL 与 RDBMS 对比 三&#xff0c;MongoDB简介 1&#xff0c; MongoDB 是什…

Python入门教程(非常详细)从零基础入门到精通,看完这一篇就够了

前言 本文罗列了了python零基础入门到精通的详细教程&#xff0c;内容均以知识目录的形式展开。 第一章&#xff1a;python基础之markdown Typora软件下载Typora基本使用Typora补充说明编程与编程语言计算机的本质计算机五大组成部分计算机三大核心硬件操作系统 第二章&…

【LeetCode】剑指 Offer 15. 二进制中1的个数 p100 -- Java Version

题目链接&#xff1a;https://leetcode.cn/problems/er-jin-zhi-zhong-1de-ge-shu-lcof/?favoritexb9nqhhg 1. 题目介绍&#xff08;15. 二进制中1的个数&#xff09; 编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回…

Systemverilog覆盖率的合并和计算方式

在systemverilog中&#xff0c;对于一个covergroup来说&#xff0c;可能会有多个instance&#xff0c;我们可能需要对这些instance覆盖率进行操作。 只保存covergroup type的覆盖率&#xff0c;不需要保存instance-specified的覆盖率coverage type和instance-specified的覆盖率…

SVM支持向量机理解_KKT条件_拉格朗日对偶_SMO算法

目录 一、支持向量机基本型&#xff08;线性可分&#xff09; 1.1 问题描述 1.2 参考资料 二、KKT条件 2.1 KKT条件的几个部分 2.1.1 原始条件 2.1.2 梯度条件 2.1.3 松弛互补条件 2.2 参考资料 三、对偶形式 四、SMO算法 五、线性不可分情形 六、核函数 一、支持…

TimeWheel时间轮算法原理及实现(附源码)

时间轮算法原理及实现前言1.时间轮核心2.简单定时器3.任务队列4.优化任务队列5.简单时间轮6.多层时间轮前言 在各种业务场景中,我们总是会需要一些定时进行一些操作,这些操作可能是需要在指定的某个时间点操作,也可能是每过一个固定的时间间隔后进行操作,这就要求我们需要有一个…

【蓝桥OJ—C语言】高斯日记、马虎的算式、第39级台阶

文章目录高斯日记马虎的算式第39级台阶总结高斯日记 题目&#xff1a; 大数学家高斯有个好习惯&#xff1a;无论如何都要记日记。 他的日记有个与众不同的地方&#xff0c;他从不注明年月日&#xff0c;而是用一个整数代替&#xff0c;比如&#xff1a;4210。 后来人们知道&am…

You Only Need 90K Parameters to Adapt Light 论文阅读笔记

这是BMVC2022的论文&#xff0c;提出了一个轻量化的局部全局双支路的低光照图像质量增强网络&#xff0c;有监督。 思路是先用encoder f(⋅)f(\cdot)f(⋅)转到raw-RGB域&#xff0c;再用decoder gt(⋅)g_t(\cdot)gt​(⋅)模拟ISP过程转到sRGB域。虽然文章好像没有明确指出&…

【蓝牙mesh】Network协议层介绍

【蓝牙mesh】Network协议层介绍 Network层简介 上一章节我们讲解了蓝牙Mesh中Lower层的功能和数据格式。 Lower层的数据往下传输就到了网络层&#xff08;Network Layer&#xff09;。网络层定义了收到Lower层的数据后&#xff0c;如何对其进行判断、封装、加密、认证&#xf…

学习(mianshi)必备-ClickHouse高性能查询/写入和常见注意事项(五)

目录 一、ClickHouse高性能查询原因-稀疏索引 二、ClickHouse高性能写入-LSM-Tree存储结构 什么是LSM-Tree 三、ClickHouse的常见注意事项和异常问题排查 一、ClickHouse高性能查询原因-稀疏索引 密集索引: 在密集索引中&#xff0c;数据库中的每个键值都有一个索引记录&…

Amazon S3 服务15岁生日快乐!

2021年3月14日&#xff0c;作为第一个发布的服务&#xff0c;Amazon S3 服务15周岁啦&#xff01;在中国文化里&#xff0c;15岁是个临界点&#xff0c;是从“舞勺之年”到“舞象之年”的过渡。相信对于 Amazon S3 和其他的云服务15周岁也将是其迎接更加美好未来的全新起点。亚…

【论文解读】如何使用1B参数的小模型吊打GPT3.5

大型语言模型 (LLM) 通过利用思维链 (CoT) 提示生成中间推理链作为推断答案的基本原理&#xff0c;在复杂推理上表现出了令人印象深刻的性能。 然而现有的 CoT 研究主要集中在语言模态上。 我们提出 Multimodal-CoT&#xff08;多模态思维链推理模型&#xff09;&#xff0c;它…

利用steam搬砖信息差赚钱,单账号200+,小白也能轻松上手!

现在很多人在做互联网而且也赚到钱了&#xff0c;但还是有很多人赚不到钱&#xff0c;这是为什么&#xff1f; 这里我不得不说一个词叫做赛道&#xff0c;也就是选择&#xff0c;选择大于努力&#xff0c;项目本身大于一切&#xff0c;90%的人都觉得直播带货赚钱&#xff0c;但…