设计模式(二)

设计模式(二)

敏捷开发模式:Refactoring to Patterns

重构特点:

1. 静态 --------> 动态
1. 早绑定 -----------> 晚绑定
1. 继承 ----------> 组合
1. 编译时依赖 --------> 运行时依赖
1. 紧耦合 -------> 松耦合

组件协作模式

通过晚期绑定,实现框架和应用间的松耦合

  1. Template Method
  2. Strategy
  3. Observer / Event
tips:基类的析构函数需要写为 虚析构函数
why:1.析构函数的作用是清理对象占用的资源;当对象生命周期结束时,析构函数会被自动调用2.为了确保当通过基类指针删除派生类对象时,能够正确地调用派生类的析构函数,从而避免资源泄漏3.多态:基类指针可以指向派生类的对象;使用基类指针删除指向的派生类对象时,如果基类的析构函数不是虚的那么只会调用基类的析构函数,而不会调用派生类的析构函数派生类可能有自己的资源需要释放,比如动态分配的内存或者打开的文件,没有正确释放,就会造成资源的泄露4.C++中对象的析构顺序是先调用派生类的析构函数,然后调用基类的析构函数如果基类的析构函数是虚的,那么当删除一个派生类对象时,首先会调用派生类的析构函数,然后是基类的析构函数
Template Method

定义一个操作中的算法的骨架(稳定), 而将一些步骤延迟(变化)到子类中

Template Method设计子类可以不改变(复用)一个算法的结构

即可重定义(override)该算法的某些步骤

// 抽象类
class Shape {
public:// 模板方法,定义了绘制图形的算法骨架void draw() {drawShape();fillShape();}// 抽象操作,由子类实现virtual void drawShape() = 0;virtual void fillShape() = 0;
};// 具体子类
class Rectangle : public Shape {
public:// 实现抽象操作void drawShape() override {// 绘制矩形的轮廓}void fillShape() override {// 填充矩形}
};class Circle : public Shape {
public:// 实现抽象操作void drawShape() override {// 绘制圆的轮廓}void fillShape() override {// 填充圆}
};

上述draw()是稳定的,不用虚函数

drawShape(), fillShape() 绘制图像,有圆的,有方的等

这是变化的,定义为虚函数;

这样就实现,算法的骨架(draw()) (稳定),变化(drawShape, fillShape)变化延迟到子类中

假设类中所有的都是稳定的,就不需要设计模式
假设类中所有都不是稳定的,也不需要设计模式了

设计模式 就是在变化和稳定中间,寻找隔离点,将变化和稳定隔离开

tips:查看代码时,找到类中哪些是变化的哪些是稳定的
这种晚绑定,c++通常用多态实现;
其实多态底层也是用函数指针实现
Strategy

motivation

软件构建中,某些对象使用的算法可能多种多样;经常改变

假设这些算法都编码到对象中,将使对象变得异常复杂;

how to 运行时根据需要透明地更改对象的算法? 实现算法和对象本身解耦合

需要用动态的思维去思考问题,设计解决方案,考虑未来;

如果使用静态的思维,只考虑当前问题,无法设计出好的方案应对未来的变化

用扩展的方式来改变;而不是修改来改变源代码

eg:使用枚举(enum)来定义不同的税率策略,然后使用if-else语句来选择相应的税率计算方法

enum TaxCountry {CHINA,USA,GERMANY
};double calculateTax(double income, TaxCountry country) {switch (country) {case CHINA:return income * 0.2;case USA:return income * 0.3;case GERMANY:return income * 0.25;default:return 0;}
}

策略模式:创建一个TaxStrategy接口,将上面的每个case实例,定义为该接口的子类

class TaxStrategy {
public:virtual ~TaxStrategy() {}virtual double calculateTax(double income) = 0;
};// 具体策略:中国税率
class ChinaTaxStrategy : public TaxStrategy {
public:double calculateTax(double income) override {return income * 0.2;}
};// 具体策略:美国税率
class USATaxStrategy : public TaxStrategy {
public:double calculateTax(double income) override {return income * 0.3;}
};// 具体策略:德国税率
class GermanyTaxStrategy : public TaxStrategy {
public:double calculateTax(double income) override {return income * 0.25;}
};// 上下文
class IncomeCalculator {
private:std::unique_ptr<TaxStrategy> taxStrategy;public:void setTaxStrategy(std::unique_ptr<TaxStrategy> taxStrategy) {this->taxStrategy = std::move(taxStrategy);}double calculateIncomeTax(double income) {return taxStrategy->calculateTax(income);}
};int main() {double income = 10000;IncomeCalculator calculator;// 假设我们选择中国税率和美国税率calculator.setTaxStrategy(std::make_unique<ChinaTaxStrategy>());std::cout << "Income tax for China: " << calculator.calculateIncomeTax(income) << std::endl;calculator.setTaxStrategy(std::make_unique<USATaxStrategy>());std::cout << "Income tax for USA: " << calculator.calculateIncomeTax(income) << std::endl;return 0;
}

定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化);

该模式使得算法可独立于使用它的客户程序(稳定) 而变化 (扩展,子类化)

if - else if 或 switch case; 通常都用策略模式来做接口; 只有那些固定下来的条件,不会再变了可以使用

而且用switch case ,if else在运行时内存装载也会出现问题,造成内存臃肿

observe

motivation

软件构建过程中,需要为某些对象建立一种 通知依赖关系

即:一个对象(观察者)的状态发生改变,所有的依赖对象(观察者对象)都会得到通知

如果这样,依赖关系过于紧密,将使软件不能很好地抵御变化

使用面向对象,将这种依赖关系弱化,形成一种稳定的依赖关系,从而实现软件体系结构的松耦合

新闻订阅服务为例,

class NewsService {
public:void addNews(std::string news) {// 添加新闻for (auto user : users) {user->notify(news); // 直接调用每个用户的notify方法}}private:std::vector<User*> users; // 紧密耦合的用户列表
};class User {
public:void notify(std::string news) {// 用户接收到新闻更新}
};
NewsService类与User类紧密耦合,如果需要添加新的用户类型或者改变通知方式,需要修改NewsService类的代码
扩展性差,如果未来有新的通知方式,例如邮件通知、短信通知等,需要在NewsService类中添加更多的代码

通过一个抽象的接口进行交互

class Subject {
public:virtual void registerObserver(Observer* observer) = 0;virtual void removeObserver(Observer* observer) = 0;virtual void notifyObservers(std::string news) = 0;
};class Observer {
public:virtual void update(std::string news) = 0;
};class NewsService : public Subject {
private:std::vector<Observer*> observers;public:void registerObserver(Observer* observer) override {observers.push_back(observer);}void removeObserver(Observer* observer) override {// 移除观察者}void notifyObservers(std::string news) override {for (auto observer : observers) {observer->update(news);}}void addNews(std::string news) {notifyObservers(news); // 通知所有观察者}
};class User : public Observer {
public:void update(std::string news) override {// 用户接收到新闻更新}
};
可扩展性:可以轻松地添加新的观察者类型,例如添加一个新的User子类来处理不同类型的用户
可维护性:由于解耦,修改NewsService或User类时,对其他类的依赖更少,因此更容易维护
灵活性:可以动态地添加或移除观察者,而不需要修改NewsService类的内部实现

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

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

相关文章

JVM(HotSpot):GC之G1垃圾回收器

文章目录 一、简介二、工作原理三、Young Collection 跨代引用四、大对象问题 一、简介 1、适用场景 同时注重吞吐量&#xff08;Throughput&#xff09;和低延迟&#xff08;Low latency&#xff09;&#xff0c;默认的暂停目标是 200 ms超大堆内存&#xff0c;会将堆划分为…

华为云弹性云服务器无法登录远程操作

遇到的问题&#xff1a; 就是你在创建弹性云服务器的时候选择了没有子网的虚拟私有云&#xff0c; 你属于误删了虚拟私有云的子网&#xff0c;自己没有注意看 如果在华为云创建弹性云服务器时选择的虚拟私有云&#xff08;VPC&#xff09;没有配置子网&#xff0c;那么在尝试远…

压力测试指南-压力测试中的性能瓶颈定位与优化

压力测试中的性能瓶颈定位与优化 在当今快速迭代的软件开发环境中&#xff0c;确保应用能够承受高并发访问和大规模数据处理变得至关重要。压力测试作为评估系统极限能力的关键手段&#xff0c;不仅能揭示潜在的性能问题&#xff0c;还能指导我们进行针对性的优化。本文将深入…

【mysql 进阶】2-1. MySQL 服务器介绍

MySQL 服务器简介 通常所说的 MySQL 服务器指的是mysqld程序&#xff0c;当运⾏mysqld后对外提供MySQL 服务&#xff0c;这个专题的内容涵盖了以下关于MySQL 服务器以及相关配置的内容&#xff0c;包括&#xff1a; 服务器⽀持的启动选项。可以在命令⾏和配置⽂件中指定这些选…

异次元v4.0

萌次元商城系统基于异次元店铺系统3.0 全新重构&#xff0c;历时两年精心打磨&#xff0c;初心不改。我们采用纯原生PHP打造了一款极具潜力的个人创业型商城系统。数据库底层经过对hyperf/database 的深度优化&#xff0c;感谢hyperf开发团队提供的强大ORM组件。模版渲染引擎采…

嵌入式C语言字符串具体实现

大家好,今天主要给大家分享一下,如何使用C语言进行字符串操作与实现。 第一:字符串相关操作实现 复制函数五个基本要素: 头文件:#include <string.h> 函数原型:strcpy(char dest[],char src[]) -----string copy 功能:把src数组中\0之前的所有字符,连同‘\…

在xml 中 不等式 做转义处理的问题

对于这种要做转义处理&#xff0c;<![CDATA[ < ]]>

DevEco Studio的使用 习题答案<HarmonyOS第一课>

一、判断题 1. 如果代码中涉及到一些网络、数据库、传感器等功能的开发,均可使用预览器进行预览。 正确(True)错误(False) 错误(False)回答正确 2. module.json5文件中的deviceTypes字段中,配置了phone,tablet,2in1等多种设备类型,才能进行多设备预览。 正确(True)…

redis第152节答疑 redis源码分析String重要总结

redis的string类型&#xff0c;如果数字大于10000&#xff0c;就不去共享整数中去取&#xff0c;然后就变成了embstr或者raw&#xff0c;为什么不是new一个redisobject,并且编码为int 对于Redis的字符串类型&#xff08;String&#xff09;&#xff0c;当字符串表示的是一个整数…

讲一讲 kafka 的 ack 的三种机制?

大家好&#xff0c;我是锋哥。今天分享关于【K讲一讲 kafka 的 ack 的三种机制&#xff1f;】面试题&#xff1f;希望对大家有帮助&#xff1b; 讲一讲 kafka 的 ack 的三种机制&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Kafka的消息确认机制&…

15分钟学 Go 第 20 天:Go的错误处理

第20天&#xff1a;Go的错误处理 目标 学习如何处理错误&#xff0c;以确保Go程序的健壮性和可维护性。 1. 错误处理的重要性 在开发中&#xff0c;错误处理至关重要。程序在运行时可能会出现各种问题&#xff0c;例如文件未找到、网络连接失败等。正确的错误处理能帮助我们…

中间人攻击(https降级攻击)和iptables命令分析

中间人攻击 以下是一个简单的中间人攻击示例&#xff0c;结合 ARP 欺骗和流量修改&#xff1a; 1. 进行 ARP 欺骗 首先&#xff0c;使用 arpspoof 进行 ARP 欺骗&#xff0c;将受害者的流量重定向到攻击者的机器上&#xff1a; sudo arpspoof -i eth0 -t 172.29.144.50 172…

Electron调用nodejs的cpp .node扩展【安全】

Electron调用nodejs的cpp .node扩展【安全】 环境&#xff1a; electron: 30.1.1 nodejs: 20.14.0前言 Electron是一个非常流行的跨平台桌面应用框架&#xff0c;它允许开发者使用Web技术来创建原生应用。然而&#xff0c;当应用需要进行高性能计算或访问系统API时&#xff…

echarts5.2.2实现 水球图表

需求背景解决思路解决效果index.vue 需求背景 需要做一个水球echarts图表效果&#xff0c;却发现echarts5以上版本已移除liquidFill类型 echarts 图表集链接 解决思路 引入 echarts-liquidfill^3.1.0 解决效果 index.vue <!--/*** author: liuk* date: 2024-10-24* de…

SVN(Subversion)的介绍和使用

SVN&#xff08;Subversion&#xff09;介绍 SVN&#xff08;Subversion&#xff09;是一个开源的版本控制系统&#xff0c;用于跟踪和控制文件和目录的更改。与传统的版本控制系统如CVS不同&#xff0c;SVN支持原子提交&#xff0c;即一次提交包含多个文件的更改&#xff0c;…

【计网】UDP Echo Server与Client实战:从零开始构建简单通信回显程序

目录 前言&#xff1a; 1.实现udpserver类 1.1.创建udp socket 套接字 --- 必须要做的 socket&#xff08;&#xff09;讲解 代码实现&#xff1a;​编辑 代码讲解&#xff1a; 1.2.填充sockaddr_in结构 代码实现&#xff1a; 代码解析&#xff1a; 1.3.bind sockfd和…

3.swoole安装【Docker】

一、拉取最新 swoole 镜像 docker pull phpswoole/swoole二、第一次启动swoole容器 docker run --name swoole phpswoole/swoole 三、 拷贝配置文件 docker cp swoole:/var/www /docker/swoole四、 停止 swoole 容器 dcoker stop swoole五、 删除第一次启动的swoole容器 d…

Vue2自定义指令及插槽

这里写目录标题 自定义指令基础语法指令的值封装v-loading指令 插槽默认插槽后备内容&#xff08;插槽的默认值&#xff09;具名插槽作用域插槽 自定义指令 自定义指令&#xff1a;自己定义的指令&#xff0c;封装一些dom操作&#xff0c;扩展额外功能 基础语法 全局注册&am…

2024年TI杯E题-三子棋游戏装置方案分享-jdk123团队-第四弹 第二题

往期回顾 前期准备 摄像头bug解决 手搓机械臂 视觉模块的封装 下面是题目部分&#xff1a; 第二问我们继续延续第一问的思路&#xff1a; 将棋子坐标与棋盘上标定的坐标进行绑定。 代码展示&#xff1a; import RPi.GPIO as GPIO import time import cv2 import numpy as…

【Qt】常用控件:按钮类控件

思维导图&#xff1a; 一、Push Button 我们可以使用 QPushButton 表示一个按钮&#xff0c;这也是当前我们最熟悉的一个控件。QPushButton继承于QAbstractButton。这个类是一个抽象类&#xff0c;是按钮的父类。 1.1 常用的属性 属性说明text按钮中的文本icon按钮中的图标ic…