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

依赖倒转

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

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

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

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

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

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

废话不多说,上例子:

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

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

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

cpp 复制代码
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

cpp 复制代码
class QQ {
private:
	std::string info;
public:
	std::string recv_info() {
		return "recv_info" + info;
	}
};

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

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

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

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

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

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

cpp 复制代码
// 依赖倒转原则
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)应当由一个或多个抽象管理和约束(设计成接口后,方便维护)

这就是依赖倒转原则。

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

相关推荐
workflower3 小时前
时序数据获取事件
开发语言·人工智能·python·深度学习·机器学习·结对编程
CoderYanger4 小时前
C.滑动窗口-求子数组个数-越长越合法——2799. 统计完全子数组的数目
java·c语言·开发语言·数据结构·算法·leetcode·职场和发展
C++业余爱好者4 小时前
Java 提供了8种基本数据类型及封装类型介绍
java·开发语言·python
林杜雨都4 小时前
Action和Func
开发语言·c#
皮卡龙4 小时前
Java常用的JSON
java·开发语言·spring boot·json
火山灿火山4 小时前
Qt常用控件(三)
开发语言·qt
利刃大大4 小时前
【JavaSE】十三、枚举类Enum && Lambda表达式 && 列表排序常见写法
java·开发语言·枚举·lambda·排序
float_六七5 小时前
Java反射:万能遥控器拆解编程
java·开发语言
han_hanker5 小时前
java 异常类——详解
java·开发语言
源码获取_wx:Fegn08955 小时前
基于springboot + vue健身房管理系统
java·开发语言·前端·vue.js·spring boot·后端·spring