深入理解依赖反转原则(DIP)

2023-12-23 10:44

本文主要是介绍深入理解依赖反转原则(DIP),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

依赖反转原则是一个比较重要的架构原则,从定义上看是要依赖于抽象,不要依赖于细节, 这个听起来很简单,好像加个接口就完事了,大家的service都是一个接口配一个实现类,是不是依赖倒置呢?很显然不是,不然今天就不用讲了

先举个例子,我们的应用会依赖很多三方的服务,DB、缓存、文件存储、短信邮件服务等等,拿缓存来说,假如使用的是redis缓存,java应用访问redis一般都会使用Jedis,如果我们的业务代码直接使用jedis,那就是依赖细节(如下图左侧),这时候公司要换技术栈,redis不用了,换成memcached,所有直接依赖JedisApi的业务代码全都得变,这就是直接依赖细节的问题:让业务层变得不太稳定

那应该怎么样保证业务层稳定呢?把依赖于细节变成依赖于抽象,业务层要的是redis吗?不!业务层要的示一个缓存服务,那就在业务层定义好缓存服务的接口,至于底层到底是redis还是memcached,业务层不关心(如上图右侧)

那谁来关心实现细节呢?外层关心,外层可以基于redis实现、也可以基于memcached实现,外层负责把业务层定义的知识(CacheService)和具体的三方服务的知识(JedisApi)互相转换、做个适配,所以这一层又叫做适配器层,如果要更换三方服务,也只是外层层变化,对业务层没有影响。

这里有个很关键的点就是,业务层定义的接口里不要有任何与细节相关的东西,不然就没有意义了,比如如果上图右侧的CacheService中还有jedis api里的东西,那就跟左侧没啥区别了,那就不叫依赖于抽象了,又变成依赖于细节了。

从依赖方向上看,上图左侧是业务层依赖于外层,右侧的依赖关系反过来了,变成了外层依赖于业务层,这就是依赖反转的本质。很明显右侧这种依赖方式,使得业务层可以保持稳定,因为规则是业务层定义的,外层只能去适配业务层定义的规则。

把依赖于redis这个例子泛化一点,应用会用到各种各样的三方服务,都是一个道理。

这样你就会发现,系统合理的依赖关系不太应该是这种从上到下的,因为领域层不应该直接依赖下面的基础设施,而应该是这种从外向内的依赖关系(这里我只画了两层同心圆,实际可能会有多层,但不妨碍我们理解)

越靠近中心,它的软件层次就越高,它越是核心业务逻辑,内部区域包含了所有的领域逻辑,而外部区域则包含了系统与外界交互的东西(比如web、rpc接口,以及数据库、文件服务、缓存这些基础设施)。不管是web、rpc还是db、cache,都是系统和外部系统交互的层,都在领域层的外层。

这里有一条非常重要的原则,就是它的依赖关系必须是外层依赖于内层,不能反过来,web层依赖于领域层,与底层设施交互的层也依赖于领域层,内层代码中不应该包含任何外层代码的知识,像类、函数、变量都不行。这样的好处很明显,业务逻辑可以保持纯净、独立,它是整个系统的核心,外层的这些东西对于业务逻辑来说都是插件,可以轻易被替换掉、并且对业务逻辑不产生影响

下面是一张六边形架构图,你会发现它要表达的意思跟前面说的是一个道理。最内层的是领域、业务逻辑,最外层的是客户端、用户、外部系统、下游基础设施

最关键的是它的依赖关系,是从外向内的,外层依赖于内层,外层了解内层的知识,反过来就不行 那怎么样保持这个依赖关系呢?用端口和适配器(所以六边形架构图又叫端口适配器架构,其实端口适配器架构更能表达它的主要思想)

端口其实就是接口,你看它端口都是在内层,意思是接口都由领域层定义,外层这一个个的适配器干嘛的呢,把外部的东西和领域层定义的互相转换,所以叫适配,这和我们前面说的是一个意思 

总结两点: 1:大家在做设计时,首先要明确内层外层的概念,核心业务逻辑就是内层的;2:有了内外层之分后,一个指导原则就是外层可以依赖内层,反过来就不行。这就是依赖反转!

tcp层需要知道http层的东西吗?ip层需要知道tcp层的东西吗?不需要!依赖反转原则应该作为大家在做架构设计时重要规则

这篇关于深入理解依赖反转原则(DIP)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/527749

相关文章

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题

《解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题》:本文主要介绍解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4... 目录未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘打开pom.XM

IDEA Maven提示:未解析的依赖项的问题及解决

《IDEAMaven提示:未解析的依赖项的问题及解决》:本文主要介绍IDEAMaven提示:未解析的依赖项的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录IDEA Maven提示:未解析的依编程赖项例如总结IDEA Maven提示:未解析的依赖项例如

一文深入详解Python的secrets模块

《一文深入详解Python的secrets模块》在构建涉及用户身份认证、权限管理、加密通信等系统时,开发者最不能忽视的一个问题就是“安全性”,Python在3.6版本中引入了专门面向安全用途的secr... 目录引言一、背景与动机:为什么需要 secrets 模块?二、secrets 模块的核心功能1. 基

Python pip下载包及所有依赖到指定文件夹的步骤说明

《Pythonpip下载包及所有依赖到指定文件夹的步骤说明》为了方便开发和部署,我们常常需要将Python项目所依赖的第三方包导出到本地文件夹中,:本文主要介绍Pythonpip下载包及所有依... 目录步骤说明命令格式示例参数说明离线安装方法注意事项总结要使用pip下载包及其所有依赖到指定文件夹,请按照以

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

深入解析 Java Future 类及代码示例

《深入解析JavaFuture类及代码示例》JavaFuture是java.util.concurrent包中用于表示异步计算结果的核心接口,下面给大家介绍JavaFuture类及实例代码,感兴... 目录一、Future 类概述二、核心工作机制代码示例执行流程2. 状态机模型3. 核心方法解析行为总结:三

Java -jar命令如何运行外部依赖JAR包

《Java-jar命令如何运行外部依赖JAR包》在Java应用部署中,java-jar命令是启动可执行JAR包的标准方式,但当应用需要依赖外部JAR文件时,直接使用java-jar会面临类加载困... 目录引言:外部依赖JAR的必要性一、问题本质:类加载机制的限制1. Java -jar的默认行为2. 类加

java -jar命令运行 jar包时运行外部依赖jar包的场景分析

《java-jar命令运行jar包时运行外部依赖jar包的场景分析》:本文主要介绍java-jar命令运行jar包时运行外部依赖jar包的场景分析,本文给大家介绍的非常详细,对大家的学习或工作... 目录Java -jar命令运行 jar包时如何运行外部依赖jar包场景:解决:方法一、启动参数添加: -Xb

C#继承之里氏替换原则分析

《C#继承之里氏替换原则分析》:本文主要介绍C#继承之里氏替换原则,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#里氏替换原则一.概念二.语法表现三.类型检查与转换总结C#里氏替换原则一.概念里氏替换原则是面向对象设计的基本原则之一:核心思想:所有引py