设计模式——模版方法和策略模式

在这里插入图片描述

前言

作为一名资深CV工程师,学会为自己减少工作量乃重中之重。但只是一味地CV,只会因为劣质代码而让自己的工作量加倍,为了将来不被繁重的维护工作而打扰自己的休息日,为了更好的节能,学习设计模式,刻不容缓。

模版方法

概念

生活中我们总是离不开各种模版的存在,作文、文章、简历的模版。正是因为这些模板的存在,我们工作的效率才大大提高,而设计模式的中模版方法正是与现实生活中的模版如出一辙。

现实中的模版一般有两类,一类为只有大体框架的,而另一类为全部都填写完的,只需要修改你想要的部分即可。我们在设计模版方法的时候,可以设计一个骨架作为调用流程,而具体的功能/算法则留给派生类来实现。或者,将那些会变化的代码逻辑封装起来,如果有需要再留给派生类更改。

定义

模版方法指的是为算法定义一个大体运行框架,而将那些会变化的代码封装起来,而留给派生类实现的一种设计模式。

代码实现

以冲泡饮料为例,冲泡的步骤基本都不会改变,只有一部分细节会有变化,正好可以使用模版方法进行设计封装。

Beverage
+void PrepareRecipe()
+~Beverage()
#void BoilWater()
#void PourInCup()
#void Brew()
#void AddCondiments()
Coffee
#void Brew()
#void AddCondiments()
Tea
#void Brew()
#void AddCondiments()

代码实例

class Beverage
{
public:void PrepareRecipe(){   // 代码运行的大体框架BoilWater();PourInCup();Brew();AddCondiments();}virtual ~Beverage() = default;
protected:// 具体实现可留给子类实现virtual void BoilWater(){std::cout << "Boiling water" << std::endl;}virtual void PourInCup(){std::cout << "Pouring into cup" << std::endl;}virtual void Brew() = 0;virtual void AddCondiments() = 0;
};class Coffee : public Beverage
{
protected:void Brew() override { std::cout << "Brewing coffee" << std::endl; }void AddCondiments() override { std::cout << "Adding sugar and milk" << std::endl; }
};class Tea : public Beverage
{
protected:void Brew() override { std::cout << "Brewing tea" << std::endl; }void AddCondiments() override { std::cout << "Adding lemon" << std::endl; }
};

钩子方法

钩子方法是模版方法的一种变体,它在框架中定义一个判断方法(钩子),让子类来决定其代码逻辑,减少了减少了外部的干预,提高了代码的灵活度与拓展性。

继续用上方的饮料代码来举例就是,不是所有都饮料中都需要添加调味剂/配料,此时可以由子类决定其算法逻辑。

代码实例

class Beverage
{
public:void PrepareRecipe(){   // 代码运行的大体框架BoilWater();PourInCup();Brew();if (NeedCondiments()) AddCondiments();}virtual ~Beverage() = default;
protected:// 钩子方法virtual bool NeedCondiments() { return false; }// 具体实现可留给子类实现virtual void BoilWater(){std::cout << "Boiling water" << std::endl;}virtual void PourInCup(){std::cout << "Pouring into cup" << std::endl;}virtual void Brew() {std::cout << "Brew some drink"; };virtual void AddCondiments() { std::cout << "Adding some condiments"; }
};class Coffee : public Beverage
{
protected:bool NeedCondiments() override { return true; }void Brew() override { std::cout << "Brewing coffee" << std::endl; }void AddCondiments() override { std::cout << "Adding sugar and milk" << std::endl; }
};

策略模式

概念

策略模式指的是将算法封装起来(成员变量/接口),使其能够根据不同情况而更换。策略模式与模版方法都需要将其算法/实现封装起来,初认可能会将其混淆,但只要认清模版方法是将实现延迟到子类实现,而策略模式是变化封装成类(接口/委托),就不会混淆了。

这里使用支付系统作为例子,随着互联网的发展,我们的支付方式越发变得丰富,如果每增加一个支付方式,支付系统都要重写代码的话,那么想必程序员都再也不用担心失业了。这种情况下使用策略模式,将支付手段封装起来,那么就正好符合OO原则中的开闭原则,系统的维护性也更好。

Payment
+Pay(int amount)
CreditCardPayment
+Pay(int amount)
PayPalPayment
+Pay(int amount)
PaymentContext
-Payment* _payment
+PayAmount(int amount)
+SetPayment(Payment* payment)

实现

class Payment
{
public:virtual void Pay(int amount) = 0; // 抽象支付方式virtual ~Payment() = default;
};// 不同支付方式继承与同一个接口
class CreditCardPayment : public Payment
{
public:void Pay(int amount) override{std::cout << "Paying " << amount << " using CreditCard" << std::endl;}
};class PayPalPayment : public Payment
{
public:void Pay(int amount) override{std::cout << "Paying " << amount << " using PayPal" << std::endl;}
};class PaymentContext
{
private:Payment* _payment{};
public:void PayAmount(int amount){if (_payment) _payment->Pay(amount);}// 根据需要变更策略void SetPayment(Payment* payment) { _payment = payment;}
};

总结

特点模板方法模式策略模式
定义将算法的固定部分提取到基类,变化部分由子类实现。将不同算法封装成独立的类,通过上下文类动态切换算法。
设计意图通过基类定义算法框架,将具体实现延迟到子类。通过将算法封装成独立的类,使其能够在运行时动态替换。
使用场景固定流程的多个步骤,其中部分步骤的实现因子类不同而不同。多种算法可以互换,且算法相对独立,变化频繁。
优点1. 代码复用性高。
2. 易于扩展新功能。
1. 符合开闭原则。
2. 代码更加灵活,易于维护和扩展。
缺点1. 继承关系较复杂。
2. 增加类的数量。
1. 增加系统复杂度。
2. 上下文类需要了解所有策略的细节。

📜博客主页:主页
📫我的专栏:C++
📱我的github:github

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

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

相关文章

数据结构_Map和Set

目录 一、搜索模型 二、Map 2.1 Map.Entry 2.2 Map 方法 2.3 Map 注意事项 三、Set 3.1 Set 方法 3.2 Set 注意事项 四、哈希表 4.1 哈希表 4.2 冲突 4.3 哈希函数设计 4.4 闭散列 4.5 开散列/哈希桶 总结 【搜索树】 二叉搜索树又称二叉排序树&#xff0c;它或…

spring-boot 整合 redisson 实现延时队列(文末有彩蛋)

应用场景 通常在一些需要经历一段时间或者到达某个指定时间节点才会执行的功能&#xff0c;比如以下这些场景&#xff1a; 订单超时提醒收货自动确认会议提醒代办事项提醒 为什么使用延时队列 对于数据量小且实时性要求不高的需求来说&#xff0c;最简单的方法就是定时扫描数据…

语音合成-TTS文字转语音(专业版)

语音合成-TTS文字转语音(专业版) 一、工具简介 *使用强大的智能AI语音库&#xff0c;合成独具特色接近真人语音的朗读音频。 *使用极具表现力和类似人类的声音&#xff0c;使文本阅读器和已启用语音的助理等方案栩栩如生。 *用途&#xff1a;这个语音工具&#xff0c;不仅可…

【C语言初阶】C语言数组基础:从定义到遍历的全面指南

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C语言 “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;C语言函数 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀数组 &#x1f4d2;1. 什么是数组…

【C++】学习笔记——AVL树

文章目录 十六、AVL树1. AVL树的概念2. AVL树节点的定义3. AVL树的插入4. AVL树的旋转5. AVL树的验证6. 完整代码测试7. AVL树的性能 未完待续 十六、AVL树 1. AVL树的概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&…

前端基础之JavaScript学习——函数的使用

大家好我是来自CSDN的前端寄术区博主PleaSure乐事&#xff0c;今天我们继续有关JavaScript的学习&#xff0c;使用的编译器为vscode&#xff0c;浏览器为谷歌浏览器。 函数的声明与使用 声明 在JavaScript当中函数的声明和其他语言类似&#xff0c;使用如下格式即可声明&…

实战篇(十):使用Processing创建可爱花朵:实现随机位置、大小和颜色的花朵

使用Processing创建可爱花朵 0.效果预览1. 引言2. 设置Processing环境3. 创建花朵类4. 实现花瓣绘制5. 绘制可爱的笑脸6. 鼠标点击生成花朵7. 完整代码8. 总结与扩展0.效果预览 在本教程中,我们将使用Processing编程语言来创建一个可爱的花朵生成器。通过封装花朵为一个类,并…

大语言模型-检索测评指标

1. MRR &#xff08;Mean Reciprocal Rank&#xff09;平均倒数排名&#xff1a; 衡量检索结果排序质量的指标。 计算方式&#xff1a; 对于每个查询&#xff0c;计算被正确检索的文档的最高排名的倒数的平均值&#xff0c;再对所有查询的平均值取均值。 意义&#xff1a; 衡量…

【STM32】按键控制LED光敏传感器控制蜂鸣器(江科大)

一、按键控制LED LED.c #include "stm32f10x.h" // Device header/*** 函 数&#xff1a;LED初始化* 参 数&#xff1a;无* 返 回 值&#xff1a;无*/ void LED_Init(void) {/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENAB…

199.二叉树的右视图(DFS)

给定一个二叉树的根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 示例 1: 输入: [1,2,3,null,5,null,4] 输出: [1,3,4] 示例 2: 输入: [1,null,3] 输出: [1,3] 示例 3: 输入: [] 输出: [] 解题…

贪心算法总结(1)

一、贪心算法简介 常用方法&#xff1a;交换论证法、数学归纳法、反证法、分类讨论 二、柠檬水找零&#xff08;交换论证法&#xff09; . - 力扣&#xff08;LeetCode&#xff09; class Solution { public:bool lemonadeChange(vector<int>& bills) {int five0,t…

【考研数学】线代满分经验分享+备考复盘

我一战二战复习都听了李永乐的线代课&#xff0c;二战的时候只听了一遍强化&#xff0c;个人感觉没有很乱&#xff0c;永乐大帝的课逻辑还是很清晰的。 以下是我听向量这一章后根据听课内容和讲义例题总结的部分思维导图&#xff0c;永乐大帝讲课的时候也会特意点到线代前后联…

TK秘籍:深度剖析机房IP与住宅IP的利与弊

大家好&#xff0c;今天我们来聊聊TikTok运营中的一个重要环节——IP地址的选择。 想象一下&#xff0c;你在TikTok上发布视频&#xff0c;就像是在一个热闹的市集上摆摊&#xff0c;而IP地址就是你的摊位位置。选对了位置&#xff0c;你的摊位就能吸引更多顾客&#xff0c;也…

最小二乘求待定位点的位置(三维环境)|MATLAB

前言 之前发过三点法求待测点位置的程序讲解&#xff0c;哪个是二维的&#xff0c;见&#xff1a;基于伪逆的三点法距离求位置&#xff0c;MATLAB源代码&#xff08;MATLAB函数&#xff09; 这里给出三维情况下的函数和测试代码。对于函数&#xff0c;输入已知锚点的位置、待…

JavaEE:Spring Web简单小项目实践三(留言板实现)

学习目的&#xff1a; 1、理解前后端交互过程 2、学习接口传参&#xff0c;数据返回以及页面展示 目录 1、准备工作 2、约定前后端交互接口 1、获取全部留言 2、发表新留言 3、实现服务器端代码 4、调整前端页面代码 5、运行测试 1、准备工作 创建SpringBoot项目&#x…

Linux 服务器管理和维护

Linux 是一个非常严谨的操作系统&#xff0c;每个目录都有自己的作用&#xff0c;这些作用是固定的&#xff0c;没有特殊情况&#xff0c;应严格执行&#xff1b; Linux 中所有东西以文件形式存储和管理&#xff0c;命令也不例外&#xff1b; 以下四个 bin 是二进制文件&…

SVM 技能测试:25 个 MCQ 用于测试数据科学家的 SVM

SVM 技能测试:25 个 MCQ 用于测试数据科学家的 SVM(2024 年更新) 一、介绍 你可以把机器学习算法想象成一个装满斧头、剑和刀片的军械库。你有各种各样的工具,但你应该学会在正确的时间使用它们。打个比方,将“线性回归或逻辑回归”视为一把能够有效地切片和切块数据但…

LeetCode 739, 82, 106

文章目录 739. 每日温度题目链接标签思路代码 82. 删除排序链表中的重复元素 II题目链接标签思路代码 106. 从中序与后序遍历序列构造二叉树题目链接标签思路二叉树的三种遍历值与索引的映射对于后序遍历的使用对于中序遍历的使用 代码 739. 每日温度 题目链接 739. 每日温度…

jenkins 插件版本冲突

一、Jenkins安装git parameter 插件重启后报错与临时解决方案 cd /root/.jenkins cp config.xml config.xml.bak vim config.xml <authorizationStrategy class"hudson.security.FullControlOnceLoggedInAuthorizationStrategy"><denyAnonymousReadAcces…

【工具使用】EMACS的verilog_mode脚本

#工作记录# 俗话说不会玩连连看的工程师不是一个好的SoC工程师。 在做集成工作的时候&#xff0c;集成连线估计是一件比较繁琐且容易出错的事情&#xff0c;连线类型定义出错、位宽问题、连线众多等等问题&#xff0c;此时使用由Veripool带来的verilog_mode简直是令人神清气爽…