设计模式-03 设计模式-依赖倒转原则案例分析


设计模式-03 设计模式-依赖倒转原则案例分析

目录

设计模式-02 设计模式-依赖倒转原则案例分析

1.定义

2.内涵

3.案例对比

4.注意事项

5.最佳实践

6.总结


1.定义

依赖倒转原则(Dependency Inversion Principle,简称DIP)高层级的模块不能依赖底层级模块的,两种层级的模块应该依赖抽象,抽象层不能依赖具体实现层,具体实现应该依赖抽象

通俗来说,DIP 意味着:

客户端代码(高层模块)不应该直接依赖于具体实现(低层模块)。
相反,客户端代码应该依赖于一个抽象,而抽象再依赖于具体实现。

+--------------+|  客户端代码  |+--------------+|V+--------------+|    抽象类    |+--------------+|V+--------------+| 具体实现类 |+--------------+

说明:

  • 客户端代码(高层模块)依赖于抽象类,而不是具体的实现类。
  • 抽象类定义了客户端代码所需的行为,而具体实现类提供了这些行为的具体实现。
  • 客户端代码通过依赖注入接收其依赖项(抽象类)。
  • 通过使用 DIP,客户端代码与具体实现类之间的耦合最小化,从而提高了代码的灵活性、可测试性和可维护性。

                   

2.内涵

DIP 的定义出发点是:

软件的灵活性:当依赖关系是固定的时,很难在不影响其他模块的情况下修改软件。
软件的可测试性:当客户端代码直接依赖于具体实现时,很难对客户端代码进行单元测试。

3.案例对比

错误的设计,经常是依赖与具体的实现。而不是依赖与接口层。以下就具体案例进行的举例说明。


bad 设计

#include <iostream>
#include <cstdio>
#include <vector>
#include <fstream>
#include <tuple>
#include <string>using namespace std;enum class Relationship{parent,child,sibling,
};struct Person{string name;
};struct Relationships  // 底层模块
{vector<tuple<Person, Relationship, Person>> relations;void add_parent_add_child(const Person& parent, const Person& child){relations.push_back({parent, Relationship::parent, child});relations.push_back({child, Relationship::child, parent});}};struct Research  // 上层模块,依赖与底层的具体实现,而不是抽象接口,违反了依赖倒转原则
{Research(Relationships& relationships){auto& relations = relationships.relations;for(auto& [first, rel, second]: relations){if(first.name == "Hohn"  && rel == Relationship::parent){cout<<"Hohn has child :"<< second.name<< endl;}}}
};int main() {Person parent{"Hohn"};Person child1{"Dhris"},child2{"Watt"};Relationships relationships;relationships.add_parent_add_child(parent,child1);relationships.add_parent_add_child(parent,child2);Research _(relationships);return 0;
}

好的设计

#include <iostream>
#include <cstdio>
#include <vector>
#include <fstream>
#include <tuple>
#include <string>using namespace std;enum class Relationship{parent,child,sibling,
};struct Person{string name;
};struct RelationshipBrowser{virtual vector<Person> find_all_children_of(const string& name) = 0;
};struct Relationships :RelationshipBrowser // 底层模块
{vector<tuple<Person, Relationship, Person>> relations;void add_parent_add_child(const Person& parent, const Person& child){relations.push_back({parent, Relationship::parent, child});relations.push_back({child, Relationship::child, parent});}vector<Person> find_all_children_of(const string &name) override {vector<Person> result;for(auto&& [first,rel,second] : relations) {if(first.name == name && rel == Relationship::parent){result.push_back(second);}}return result;}};struct Research  // 上层模块不在依赖 Relationships ,而是依赖接口:RelationshipBrowser
{Research(RelationshipBrowser& browser){for(auto& child: browser.find_all_children_of("Hohn")){cout<<"Honh has child:"<< child.name<<endl;}}
//    Research(Relationships& relationships){
//        auto& relations = relationships.relations;
//        for(auto& [first, rel, second]: relations){
//            if(first.name == "Hohn"  && rel == Relationship::parent){
//                cout<<"Hohn has child :"<< second.name<< endl;
//            }
//        }
//    }
};int main() {Person parent{"Hohn"};Person child1{"Dhris"},child2{"Watt"};Relationships relationships;relationships.add_parent_add_child(parent,child1);relationships.add_parent_add_child(parent,child2);Research _(relationships);return 0;
}

两种设计思路输出结果一样,但是好的设计遵循了依赖倒转原则,代码更灵活

Honh has child:Dhris
Honh has child:Watt
4.注意事项


在具体开发中,应用依赖倒转原则(DIP)时需要注意以下几点:

  • 明确接口和实现之间的关系:确保接口定义了客户端代码所需的行为,而实现提供了这些行为的具体实现。
  • 使用抽象类或接口来定义抽象:避免使用具体类作为抽象,因为这会违反 DIP。
  • 通过依赖注入传递依赖项:使用依赖注入框架或手动将依赖项传递给客户端代码,而不是让客户端代码直接创建依赖项。
  • 避免循环依赖:确保依赖关系是单向的,即高层模块不依赖于低层模块,低层模块也不依赖于高层模块。
  • 考虑性能影响:在某些情况下,使用 DIP 可能会有轻微的性能开销,因此在应用 DIP 时需要权衡利弊。

5.最佳实践

DIP 有以下优点:

  • 提高代码的灵活性:通过使用抽象,我们可以轻松地更改具体实现,而无需修改客户端代码。
  • 提高代码的可测试性:通过使用抽象,我们可以对客户端代码进行单元测试,而无需依赖于具体实现。
  • 减少耦合:DIP 减少了客户端代码与具体实现之间的耦合,从而提高了代码的可维护性和可重用性。

6.总结

DIP 并不是万能的,在某些情况下可能会有一些局限性:
(1)轻微的性能开销:使用 DIP 可能会有轻微的性能开销,因为需要额外的开销来管理依赖项。
(2)过度抽象:过度抽象可能会使代码难以理解和维护。

过度抽象就会使得系统复杂化,不便于后续优化。

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

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

相关文章

ModuleNotFoundError: No module named ‘pyautogui‘

ModuleNotFoundError: No module named pyautogui 这个错误意味着你的Python环境中没有安装pyautogui这个模块。pyautogui是一个用于程序化地控制鼠标和键盘的Python模块&#xff0c;常常用于自动化任务、GUI测试等场景。 为了解决这个问题&#xff0c;你需要安装pyautogui模块…

Java实现以图识图功能模块(简单案例)

由于完整的以图识图系统代码较长且复杂&#xff0c;这里仅提供使用OpenCV进行特征提取和比较的简化版示例代码。 1. 引入OpenCV Java库 首先&#xff0c;你需要在项目中引入OpenCV的Java库。这通常涉及将OpenCV的jar包添加到项目的类路径中。 2. 提取图像特征 使用OpenCV的…

机器学习实战-聚类算法

聚类算法是一种无监督学习的算法&#xff0c;用于将数据集中的数据分成不同的聚类或组。聚类算法是数据挖掘和机器学习领域中常见的技术之一&#xff0c;具有广泛的应用。 以下是聚类算法的一些知识点&#xff1a; 聚类算法的目的是将数据集划分为不同的组&#xff0c;使得组内…

C++ 矩阵

目录 了解矩阵的数学原理&#xff08;大学线性代数&#xff09; 矩阵及转置矩阵 矩阵乘法 矩阵快速幂 相伴矩阵模板 [相伴矩阵,快速矩阵幂]CSES1722 Fibonacci Numbers 了解矩阵的数学原理&#xff08;大学线性代数&#xff09; 矩阵及转置矩阵 这里A就是一个矩阵&…

uniapp 桌面应用插件 Ba-Launcher

简介&#xff08;下载地址&#xff09; Ba-Launcher 可以让你的应用成为简单的桌面应用&#xff0c;如需扩展功能&#xff0c;请联系我。 截图展示 可关注博客&#xff0c;实时更新最新插件&#xff1a; uniapp 常用原生插件大全 使用方法 使用方法也很简单&#xff0c;在插…

PG数据库结构与oracle比较

1.数据库集簇逻辑结构 数据库集簇概念&#xff1a;一个大的数据库是由若干个小的数据库组成&#xff0c;实现数据的隔离存放&#xff0c;在概念上应该是与mysql一样的 在mysql中可以用show database列出数据库 PG中用\l 数据库对象存放在数据库中&#xff1a; PG中的所有数据…

题解:CF1951E(No Palindromes)

题解&#xff1a;CF1951E&#xff08;No Palindromes&#xff09; 题目翻译&#xff1a;给定一个长度为 n n n 的字符串 s s s&#xff0c;询问是否可以将其分成若干份&#xff0c;使得每一份都不是回文串。若可以&#xff0c;输出 YES 并给出任意一组方案&#xff1b;若不可…

【BASH 常用脚本系列3 -- shell实现查找目录并进入目录】

文章目录 shell实现查找目录并进入目录脚本实现shell实现查找目录并进入目录 在linux中终端下工作,如果要进入一个深度很深的目录的话需要 cd ./xx/xx./.. 执行很多次,用起来很麻烦,有些人就建议使用autojump来实现,但是autojump 的一个缺点是:如果本地有多套代码,只是它…

计算机英文论文常见错误写作习惯3

目录 第一部分 Numbers and Equations ‘such as’ and ‘etc.’ 第二部分 第一部分 Numbers and Equations 两个非常常见的错误是关于阿拉伯数字和方程式的表示。中国作家通常写阿拉伯数字&#xff0c;而不是拼出单词。然而&#xff0c;使用阿拉伯数字本身并不是一个错误…

【Shell】part1-Shell-基础入门篇

part1-基础入门 HelloWorld 创建,编写,运行 Shell 脚本 vim test.sh#!/bin/bash echo "Hello World !"# 1.作为可执行程序运行 chmod ax ./test.sh #使脚本具有执行权限 ./test.sh #执行脚本# 2.作为解释器参数运行 /bin/sh test.sh /bin/php test.php变量 变…

【Python】 逻辑回归:从训练到预测的完整案例

我把我唱给你听 把你纯真无邪的笑容给我吧 我们应该有快乐的 幸福的晴朗的时光 我把我唱给你听 用我炙热的感情感动你好吗 岁月是值得怀念的留恋的 害羞的红色脸庞 谁能够代替你呀 趁年轻尽情的爱吧 最最亲爱的人啊 路途遥远我们在一起吧 &#x1f3b5; 叶…

如何利用STM32F103实现太阳板的光线追踪

如何利用STM32F103实现太阳板的光线追踪 太阳能发电效率的提升一直是绿色能源领域的研究热点。通过太阳板的光线追踪技术&#xff0c;我们可以确保太阳板始终面向太阳&#xff0c;从而最大化其接收阳光的面积&#xff0c;提高能源转换效率。本文将介绍如何利用STM32F103微控制…

Redis第15讲——RedLock、Zookeeper及数据库实现分布式锁

由于篇幅原因&#xff0c;在上篇文章我们只介绍了redis实现分布式锁的两种方式——setnx和Redission&#xff0c;并对Reidssion加锁和看门狗机制的源码进行了分析&#xff0c;但这两种方案在极端情况下都会出现或多或少的问题。那么针对上述问题&#xff0c;比较主流的解决方案…

Linux服务器基本操作

Linux下服务器基本操作指令 Vim 文件名 进入 i编辑 esc退出编辑 &#xff1a;wq 保存退出 Cp -r文件夹 path 完整或…/ Cp 文件 path pwd 查看当前目录 rm -rf 2005 删除文件夹 Mkdir 创建文件夹 squeue查看提交队列 tail -f rsl.out.0000 在运行当前目录下查看进度 Scancel j…

用Scrapy 从数据挖掘到监控和自动化测试

Scrapy 是一个 BSD 许可的快速高级网络爬虫和网络抓取框架&#xff0c;用于抓取网站并从其页面中提取结构化数据。它可以用于广泛的用途&#xff0c;从数据挖掘到监控和自动化测试。 安装scrapy pip install scrapy 爬虫示例 示例代码写入文件 import scrapyclass QuotesSp…

Kylin Linux V10 SP1 aarch64部署k8s集群严重bug

目录 1.部署方式 2.遇到问题 3.问题解决 1.部署方式 通过sealos方式部署 2.遇到问题 适配Kylin Linux V10 SP1 aarch64部署pod 不少出现CrashLoopBackOff 通过命令: kubectl describe pod xxx -n default 查看,发现报错如下: Error response from daemon: OCI …

简约大气的全屏背景壁纸导航网源码(免费)

简约大气的全屏背景壁纸导航网模板 效果图部分代码领取源码下期更新预报 效果图 部分代码 <!DOCTYPE html> <html lang"zh-CN"> <!--版权归孤独 --> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible…

工厂模式和策略模式区别

工厂模式和策略模式都是面向对象设计模式&#xff0c;但它们的目的和应用场景有所不同。 工厂模式是一种创建型设计模式&#xff0c;旨在通过使用一个工厂类来创建对象&#xff0c;而不是直接使用new关键字来创建对象。这样做可以使系统更容易扩展和维护&#xff0c;因为新的对…

图论之最短路算法模板总结

来个大致的分类&#xff1a; 朴素的迪杰斯特拉&#xff1a; 实现&#xff1a; 我们让s表示当前已经确定的最短距离的点&#xff0c;我们找到一个不在s中的距离最近的点t&#xff0c;并用t来更新其他的点。 下面是AC代码&#xff1a; #include<bits/stdc.h> using nam…

C语言-整体内容简单的认识

目录 一、数据类型的介绍二、数据的变量和常量三、变量的作用域和生命周期四、字符串五、转义字符六、操作符六、常见的关键字6.1 关键字static 七、内存分配八、结构体九、指针 一、数据类型的介绍 sizeof是一个操作符&#xff0c;是计算机类型/变量所占内存空间的大小   sc…