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

C++通透讲解设计模式:依赖倒转(1)

依赖倒转

这是我认为的SOLID里面最重要的一个原则,当你掌握这种设计方式之后,会让别人在调用你的代码时爽很多。

C++20设计模式这本书中,依赖倒转写的很抽象。我这里将他的概念列出:

  1. 高层模块不应该依赖底层模块,它们都应该依赖抽象接口。
  2. 抽象接口不应该依赖细节,细节应该依赖抽象接口。

看到这两句话就觉得很抽象,对吧?

其实我总结出一个规律描述上面的关系:

类与类之间的关联行为应当由一个或者多个抽象管理和约束(我们最后再解释这句话)

废话不多说,上例子:

首先有个需求,我们现在要实现一个人接收电子邮件或者qq信息的场景,正常来说你可能会这样设计:

  1. 电子邮件和QQ还有人,三个对象本身都应有个类
  2. 邮件和QQ作为单独的一个对象,应该有一个方法getinfo获取他们的信息
  3. 而人作为对象,应该有执行这个getinfo的能力。

根据上面的逻辑,我们应该如下设计:


class Email {
public:
	void getInfo() {
		cout << "Email getInfo" << endl;
	}
};

class QQ {
public:
	void getInfo() {
		cout << "QQ getInfo" << endl;
	}
};

class Person {
public:
	void receive(Email& email) {
		email.getInfo();
	}
	void receive(QQ& qq) {
		qq.getInfo();
	}
};

看起来很不错!!写的代码也很整齐!没有学习设计模式之前我也是这样认为的。

但是我们忽略了一个问题:

  • 高层与低层之间的依赖关系

这会造成什么呢?

如果某一天,你不再负责维护这个项目,而是转而去做别的。这个项目交给一个其他人来维护。

此时正好有个需求是——添加一种新的软件叫做kk语音,那新来的那个家伙是不是就要开始写代码了?

此时,他也写了一个函数叫做recv_info

class QQ {
private:
	std::string info;
public:
	std::string recv_info() {
		return "recv_info" + info;
	}
};

之后这个人又走了,又来一个新人…

长此以往,由于代码变得很多,新的程序员需要将经历放在实现功能上时往往就会忽略整个项目的结构,加上没有对应的接口来进行规范,就出现一种方法类乱设计的情况。

知道某一天来了一个人,遇到主函数逻辑需要大改的情况,调用这个方法时发现什么呢?有的命名为getinfo有的命名为recv_info有的方法是返回值传参,有的是传出参数传参…

当然,现实情况不一定是这样,上面的例子是一种夸张的比喻(但是不是说没人这样写)。

最正确的方法应该是什么呢?

将这个方法设计成抽象类(抽象接口),让底层和高层的实例类(也被称为细节)都去依赖它。

// 依赖倒转原则
class IReceiver {
public:	
	virtual ~IReceiver() = default;
	virtual void getInfo() = 0;
};

class Email : public IReceiver {
public:
	void getInfo() override {
		cout << "Email getInfo" << endl;
	}
};

class QQ : public IReceiver {	
public:
	void getInfo() override {
		cout << "qq getInfo" << endl;
	}
};

class Person {
public:
	// 里氏替换原则
	void receive(IReceiver& receiver) {
		receiver.getInfo();
	}
};

这样写的话,后来的人再添加功能时,就会优先注意到IReceiver这个接口,从而根据这个接口来写对应的函数,如果接口的返回值不能满足它,它会额外设计一个分离开的接口,促使下一个人遵守另一条设计原则——接口隔离原则。

总而言之,返回我一开始总结的那句话:会发现高层和底层模块之间的依赖就是类与类之间的关联行为(getinfo)应当由一个或多个抽象管理和约束(设计成接口后,方便维护)

这就是依赖倒转原则。

当然,更先进的做法其实还有一种方法,叫做依赖注入,下次有机会再讲!


http://www.kler.cn/a/507522.html

相关文章:

  • 32单片机综合应用案例——基于GPS的车辆追踪器(三)(内附详细代码讲解!!!)
  • 数据仓库复用性:业务需求复用性设计
  • Spring Boot 实战篇(四):实现用户登录与注册功能
  • 【Elasticsearch】全文搜索与相关性排序
  • Jmeter 简单使用、生成测试报告(一)
  • go chan底层分析
  • 【MySQL】:事务
  • 什么是 OpenSSL?OpenSSL 如何工作?
  • hive连接mysql报错:Unknown version specified for initialization: 3.1.0
  • OpenCV入门学习
  • 讲一下ZooKeeper的持久化机制?
  • 【Linux】进程结束和进程等待
  • 银行卡 卡号展示隐藏****
  • 【大语言模型】ACL2024论文-38 从信息瓶颈视角有效过滤检索增强生成中的噪声
  • C# OpenCV机器视觉:极大值抑制
  • 销售团队如何选择销售业绩统计表模板?
  • PouchDB + Dexie.js:构建高效的离线优先同步方案
  • 《探索烟雾目标检测开源项目:技术与应用的深度剖析》
  • STM32网络通讯之LWIP下载移植项目设计(十六)
  • Thrustmaster Hotas Warthog飞行操作杆开发
  • [cg] glDrawBuffers MRT的应用
  • 【0x3D】HCI_Remote_Host_Supported_Features_Notification事件详解
  • 1.7 ChatGPT:引领AI对话革命的致胜之道
  • 冯康简介,中国有限元先驱
  • 基于HPM6750实现一个UVC和MSC的符合类设备
  • Flink CDC解决数据库同步,异常情况下增量、全量问题