C# 设计模式六大原则之依赖倒置原则

news2024/10/2 1:29:51

总目录


前言


1 基本介绍

1. 定义

依赖倒置原则 Dependence Inversion Principle,简称:DIP。

依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。

2.分析

在理解依赖倒置原则之前,我们先要理解以下几个概念:

  • 依赖:表示类之间的关系总共有6种,依赖是其中的一种,当A使用了一个B的时候,就是依赖关系。依赖在代码中具体体现为在A类中申明了一个B类作为成员变量或在A类某个方法中使用B类作为参数。

  • 高层模块和底层模块:低层模块 就是被使用的那一方,高层模块就是使用者,这是一个相对的概念。比如 A类使用B类,那么B类就是底层模块,A类就是高层模块

  • 抽象和细节:在C#中抽象一般就是只抽象类和接口,细节就是对应的实现类

有了以上的介绍,我们可以说:

依赖倒置原则就是程序逻辑在传递参数或关联关系时,尽量引用高层次的抽象,不使用具体的类,即使用接口或抽象类来引用参数,声明变量以及处理方法返回值等。

依赖原则说简单点,就是面向接口编程(抽象类和接口 这里统称接口)。接口相对来说是稳定的,而实现类是具体的业务实现,业务的变化是不稳定的。

注:关于类之间的关系说明,详见UML类图 详解。

3. 目的

依赖倒置原则可以使我们的架构更加的稳定、灵活,也能更好地应对需求的变化。因为相对于细节的多变性,抽象的东西是稳定的。所以以抽象为基础搭建起来的架构要比以细节为基础搭建起来的架构要稳定的多。

使用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性。

2 案例分析

1. 案例1

这里我们以父亲给3岁孩子读 儿童故事书 为例

	//儿童故事书类
    public class Storybook
    {
    	//获取故事书内容
        public string GetContent()
        {
            string content = "孙悟空大闹天宫";
            return content;
        }
    }
	//父亲类
    public class Father
    {
    	// 爸爸读书 的方法
        public void Read()
        {
            Console.WriteLine("爸爸开始给孩子讲故事啦!");
            Storybook storybook = new Storybook();
            Console.WriteLine($"爸爸正在读:{storybook.GetContent()}");
        }
    }

客户端调用:

        static void Main(string[] args)
        {
            Father father = new Father();
            father.Read();
            Console.ReadKey();
        }

很好实现了需求,我们来看看依赖关系,Father类中引用了Storybook类,因此
Father是直接依赖于Storybook类。

这时需求变更了,妈妈说让爸爸给孩子讲讲编程启蒙书,于是吭哧吭哧修改代码如下:

	//定义一个编程启蒙书类
    public class CodingBook
    {
        public string GetContent()
        {
            string content = "二进制,ascii...";
            return content;
        }
    }

    public class Father
    {
    	//由于需求变了,因此这里的代码需要做修改
        public void Read()
        {
            //Console.WriteLine("爸爸开始给孩子讲故事啦!");
            //Storybook storybook = new Storybook();
            //Console.WriteLine($"爸爸正在读:{storybook.GetContent()}");

            Console.WriteLine("爸爸开始给孩子讲编程启蒙书啦!");
            CodingBook codingBook = new CodingBook();
            Console.WriteLine($"爸爸正在读:{codingBook.GetContent()}");

        }
    }

按照这种初级的编程方式我们发现,只要需求稍微发生变化,代码就需要做改动,导致程序极其的不稳定。如果后面需求又变了,需要给孩子读英语启蒙,数学启蒙书等;再者说不定爸爸要加班,需要妈妈来给孩子读书那怎么办呢?其实在这个案例中,具体读什么读物是会发生变化,这就是细节,具体谁来给孩子读也会发生变化,这些细节都是会发生变化的,不稳定的。但是给孩子读书这个抽象的行为是稳定的。如果这时候还是使用传统的OOP思想来解决问题,那么会导致程序不断的在修改,因此我们需要用到依赖倒置原则。

现在我们再来看看使用依赖倒置原则后的代码吧。

首先定义出抽象的部分


	//既然是面向接口编程,我们先将可以抽象的对象和行为全部定义出来

	//定义 读物 的抽象类
    public abstract class AbstractBook
    {
    	//定义实现类必须实现的抽象方法
    	//对于读物研而言就是输出读物内容
        public abstract string GetContent();
    }

	//定义 负责陪孩子读书的人 的抽象类
    public abstract class AbstractPerson
    {
    	//定义一个AbstractBook变量,这就是对依赖于抽象
    	//无论AbstractBook的实现类如何变化,这里的代码是可以稳定运行的
        public AbstractBook abstractBook;
        public AbstractPerson(AbstractBook book)
        {
            this.abstractBook = book;
        }
        public abstract void Read();
    }

然后定义出实现的部分

    public class Storybook : AbstractBook
    {
        public override string GetContent()
        {
            string content = "孙悟空大闹天宫";
            return content;
        }
    }

    public class CodingBook:AbstractBook
    {
        public override string GetContent()
        {
            string content = "二进制,ascii...";
            return content;
        }
    }

    public class Father : AbstractPerson
    {
        public Father(AbstractBook book) : base(book)
        {
        }

        public override void Read()
        {
            Console.WriteLine($"{this.GetType().Name}开始给孩子讲{abstractBook.GetType().Name}啦!");
            Console.WriteLine($"正在读:{abstractBook.GetContent()}");
        }
    }

客户端调用:

        static void Main(string[] args)
        {
            //爸爸给孩子讲故事书
            AbstractBook book=new Storybook();
            AbstractPerson father = new Father(book);
            father.Read();

            Console.ReadKey();
        }

我们现在来看看,使用依赖倒置原则后的代码,应对需求变化的能力如何?

需求变化1:需要爸爸给孩子读数学启蒙书了,我们只需新增如下代码:

    public class Mathsbook : AbstractBook
    {
        public override string GetContent()
        {
            string content = "认识数字,加减法...";
            return content;
        }
    }

客户端调用:

        static void Main(string[] args)
        {
            //爸爸给孩子讲数学启蒙书
            AbstractBook book=new Mathsbook();
            AbstractPerson father = new Father(book);
            father.Read();

            Console.ReadKey();
        }

相较于之前的代码,只是将AbstractBook book=new Storybook();替换为了AbstractBook book=new Mathsbook(); 不仅改动的代码少,而且有了对应变化的能力。

需求变化2:爸爸不在家,今天需要妈妈来给孩子读故事书了,我们只需新增一个Mother类

    public class Mother : AbstractPerson
    {
        public Mother(AbstractBook book) : base(book)
        {
        }

        public override void Read()
        {
            Console.WriteLine($"{this.GetType().Name}开始给孩子讲{abstractBook.GetType().Name}啦!");
            Console.WriteLine($"正在读:{abstractBook.GetContent()}");
        }
    }

客户端调用:

        static void Main(string[] args)
        {
            AbstractBook book=new Storybook();
            AbstractPerson father = new Mother(book);
            father.Read();

            Console.ReadKey();
        }

高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。

在上面的案例中:
在传统代码中 Father 中使用到了Storybook,Father相对于Storybook是高层模块,Father依赖于Storybook,就是高层依赖低层;Father和Storybook都是细节,且它们之间的依赖是细节间的依赖,这就违背了依赖倒置原则;
因此我们定义了AbstractPerson和AbstractBook两个抽象,AbstractPerson 对于 读物的依赖,不再依赖于具体的实现类如Storybook,CodingBook等,这些属于细节,而是依赖它们抽象类AbstractBook,这就符合 高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节。
我们再来看看Father类

    public class Father : AbstractPerson
    {
        public Father(AbstractBook book) : base(book)
        {
        }

        public override void Read()
        {
            Console.WriteLine($"{this.GetType().Name}开始给孩子讲{abstractBook.GetType().Name}啦!");
            Console.WriteLine($"正在读:{abstractBook.GetContent()}");
        }
    }

Father 类是一个细节,但是在Read 方法中使用了abstractBook.GetContent()抽象类的GetContent方法。这就是细节应该依赖抽象。

2. 案例2

在这里插入图片描述
在传统的三层架构中,层与层之间是相互依赖的,UI层依赖于BLL层,BLL层依赖于DAL层。分层的目的是为了实现“高内聚、低耦合”。传统的三层架构只有高内聚没有低耦合,层与层之间是一种强依赖的关系,这也是传统三层架构的一种缺点。如果低层发生变化,可能上面所有的层都需要去修改,而且这种传统的三层架构也很难实现团队的协同开发,因为上层功能取决于下层功能的实现,下面功能如果没有开发完成,则上层功能也无法进行。

当我们使用依赖倒置原则后,上面的依赖关系就变成了下图这种依赖关系。
在这里插入图片描述
我们知道,在传统的三层架构里面,UI层直接依赖于BLL层,BLL层直接依赖于DAL层;由于每一层都是依赖下一层的实现,所以说当下层发生变化的时候,它的上一层也要发生变化。
当我们使用依赖倒置原则后UI、BLL、DAL三层之间没有直接的依赖关系,而是依赖于接口。具体实现就是应先确定出接口,DAL层抽象出IDAL接口,BLL层抽象出IBLL接口,这样UI层依赖于IBLL接口,BLL实现IBLL接口,BLL层依赖于IDAL接口,DAL实现IDAL接口。


结语

希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


参考资料:
C#编程:依赖倒置原则DIP
C#设计模式六大原则 - 依赖倒置
C# IoC学习笔记

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

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

相关文章

GO之基本语法

一、Golang变量 一)变量的声明:使用var关键字 Go语言是静态类型语言 Go语言的基本类型有: boolstringint、int8、int16、int32、int64uint、uint8、uint16、uint32、uint64、uintptrbyte // uint8 的别名rune // int32 的别名 代表一个 Unic…

CTF-web 基础 网络协议

网络协议 OSI七层参考模型:一个标准的参考模型 物理层 网线,网线接口等。 数据链路层 可以处理物理层传入的信息。 网络层 比如IP地址 传输层 控制传输的内容的传输,在传输的过程中将要传输的信息分块传输完成之后再进行合并。 应用…

sql语句精讲

目录 一、MySql的细节知识 SQL语句的结束 SQL语句的大小写 SQL语句中的空格 SQL查询结果并排序 WHERE和ORDER BY的位置 常用数据类型 MYSQL比较运算符 MYSQL算术运算符​编辑 MYSQL逻辑运算符 运算符的优先级 二、MySql数据库的基本操作 1.创建数据库 2.选择数据…

kubenetes证书续签

转载:k8s证书续签 1 检查证书年限 kubeadm certs check-expiration 2 对现有证书进行备份 # 备份kubeadm cp -ra /usr/bin/kubeadm /usr/bin/kubeadm.bak # 备份证书 cp -ra /etc/kubernetes /etc/kubernetes.bak 3 重新编译kubeadm 拉取k8s仓库代码 git clone…

【数据分析--Pandas实战指南在真实世界数据中的应用】

前言: 💞💞大家好,我是书生♡,本阶段和大家一起分享和探索数据分析—基础介绍,本篇文章主要讲述了:数据分析的介绍,Python开源库,配置Jupyter,Pandas读取数据…

echarts 漏斗图 渐变金字塔

使用echarts实现金字塔效果,颜色渐变,左右显示其对应的值 效果: 如果要实现一个正三角的形状,需要在data数组中,将value赋值成有序递增,bl代表他的分值,显示在左侧。 var data [{name: "…

NSS [SWPUCTF 2022 新生赛]file_master

NSS [SWPUCTF 2022 新生赛]file_master 开题,一眼文件上传。 network看看返回包。后端语言是PHP。 除了文件上传还有个查看文件功能。 起手式查询/etc/passwd,发现查询方法是GET提交参数,后端使用file_get_contents()函数包含文件。同时有op…

MySQL基础练习题21-按日期分组销售产品

目录 题目 准备数据 分析数据 总结 题目 找出每个日期、销售的不同产品的数量及其名称。每个日期的销售产品名称应按词典序排列。 返回按 sell_date 排序的结果表。 准备数据 ## 创建库 create database db; use db;## 创建表 Create table If Not Exists Activities (s…

初阶数据结构4 二叉树

1. 树 1.1 树的概念与结构 树是⼀种⾮线性的数据结构,它是由 n(n>0) 个有限结点组成⼀个具有层次关系的集合。把它叫做树是因为它看起来像⼀棵倒挂的树,也就是说它是根朝上,⽽叶朝下的。 有⼀个特殊的结点&#…

Flink实时数仓(六)【DWD 层搭建(四)交易域、互动域、用户域实现】

前言 今天的任务是完成 DWD 层剩余的事实表;今年的秋招开得比往年早,所以要抓紧时间了,据了解,今年的 hc 还是不多,要是晚点投铁定寄中寄了; 今天还是个周末,不过记忆里我好像整个大学都没有好好…

如何实现ElementUI表单项label的文字提示?

在Vue和ElementUI的丰富组件库中,定制化表单是常见的需求之一。那么如何在表单项label后添加文字提示,以提升用户体验呢? 首先我们来看一下效果图: 这里我们鼠标移动到❓图标上就会出现提示 在 ElementUI 中,el-form-item 组件允许使用 slot 自定义 label。通过在 el-fo…

Boost:asio网络编程从同步到异步

文章目录 同步服务器客户端服务端 异步服务器(有问题)异步服务器优化 在学TCP的时候&#xff0c;写的第一个服务器就是一个echo服务器&#xff0c;那在Boost网络编程中&#xff0c;自然也先写一个echo服务器练练手 同步服务器 客户端 #include <iostream> #include &l…

<数据集>BDD100K人车识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;15807张 标注数量(xml文件个数)&#xff1a;15807 标注数量(txt文件个数)&#xff1a;15807 标注类别数&#xff1a;7 标注类别名称&#xff1a; [pedestrian, car, bus, rider, motorcycle, truck, bicycle] 序号…

Java-变量,运算符,输入与输出

目录 一&#xff0c;语法基础 1.基本Java程序 2.语法基础 2.1 变量 2.2 常量限制(fiinal)类比C中的const 2.3 类型转化 2.4 运算符 2.5 表达式 2.5 输入与输出 2.5.1 输入 2.5.2 输出 一&#xff0c;语法基础 1.基本Java程序 public class Main{public static void…

差分放大电路

目录 引出 复合管 直接耦合放大电路 问题: 怎么抑制 初代电路(已引入负反馈之后) 分析 怎么解决 镜像电路 两个概念 分析直流通路: 分析交流电路: 差分放大电路的分析 交流通路 简化 H参数等效 可以得到 其他接法 引出 复合管 目的:获得更大的放大倍数 多只…

3个二创文案生成器,让文案创作变简单

在当今数字时代&#xff0c;内容创作已经成为了一项非常重要的工作。无论是为了推广产品、营销服务&#xff0c;还是仅仅为了吸引读者&#xff0c;优质的文案都是至关重要的。然而&#xff0c;对于许多人来说&#xff0c;写出令人印象深刻的文案并不容易。这就是为什么二创文案…

基于Django框架的挂号诊疗系统(源码+论文+部署讲解等)

博主介绍&#xff1a;✌全网粉丝10W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术栈介绍&#xff1a;我是程序员阿龙&#xff…

【优秀python系统案例】基于python Flask的电影票房数据爬取与可视化系统的设计与实现

第1章 绪论 1.1 研究背景及意义 进入21世纪&#xff0c;特别是第二个十年&#xff0c;世界互联网取得了惊人的快速发展。根据分析师玛丽米克尔 (Mary Mikel) 2016年发布的一份互联网趋势报告&#xff0c;到2016年年中&#xff0c;全球互联网用户超过30亿&#xff0c;约占全球…

职场上的人情世故,你知多少?

对于职场新人来说&#xff0c;在学习人情世故时&#xff0c;不仅要学会哪些事情该做&#xff0c;还需要知道哪些事情千万不能做&#xff0c;这样才能让自己起码不会得罪别人&#xff0c;甚至得到更多的晋升机会。学会下面的四大职场规则&#xff0c;能让你的职场生涯更顺利。 …

MUSE Multi-View Contrastive Learningfor Heterophilic Graphs

发表于:CIKM 推荐指数: #paper/⭐ 一句话总结:融合了GCN(A,X)和GCN(A,I),创新性不足,因此只能B会 流程: 融合部分: h i f h i s λ i h i c h_i^fh_i^s\lambda_ih_i^c hif​his​λi​hic​ 由于有n个 λ \lambda λ.因此作者加了如下优化: L ϕ ∑ i 1 N λ i s ( h i …