设计原则-依赖倒置原则

news2024/9/22 9:31:08

如同人体结构一样,项目代码也是需要有结构的,如原子逻辑块(不可再分代码块)、方法、类、模块等。结构要么是由成熟的框架搭建起来,要么自己手动划分,但是都需要保证下层模块的变动时不会影响上层模块。注意:这里所说的模块和项目代码结构中不完全一样,这里可以至结构中各个部分,比如原子逻辑块、方法等。
按照生活的正常逻辑来说,上层模块依赖于下层模块(即,“依赖正置”)是没问题的,下层模块实现的功能就是提供给上层模块使用。但是由于需求的不断变化,下层模块功能的变动可能会影响到上层模块,继而导致整体功能的不可用。为解决这个问题,项目的各模块之间需遵循依赖倒置原则,下面我们就逐步讲述下什么是依赖倒置原则,以及如何解决这个问题的。
在这里插入图片描述

一、依赖倒置原则概念

依赖倒置原则(Dependence Inversion Priciple, DIP)原始定义是:

High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstraction should not depend upon details. Details should depend upon abstractions.

定义内容具体描述了三个设计方面:

  1. 高层模块不应该直接依赖底层模块,两者都应该依赖其抽象。
  2. 抽象不应该依赖于细节
  3. 细节应该依赖于抽象

在这里插入图片描述
根据在软件设计中"加一层"的思路办法,这里依赖倒置也是如此,两个模块直接依赖可能会有问题,那就引入抽象层。什么是抽象?什么是细节呢?直观来说,在Java语言中,抽象就是指接口或抽象类,均不能实例化;细节就是具体实现类。
将依赖上升到抽象层。为保证行为(功能)和之前类似,抽象和具体实现细节也有两个约束。第一个是抽象不依赖于细节,这表明了细节的改动不应该影响到抽象含义的变化,上层模块的影响评估基本按照抽象含义的变动范围进行评估。第二个是细节应该依赖于抽象,这表明具体实现细节应该符合抽象含义,抽象含义变化时,细节也需要跟随改动。
回头来看,“依赖倒置”并不是说模块之间的依赖倒置,模块之间依然是上层依赖于下层。这里的倒置指的是抽象和细节之间的依赖关系倒置了。这种倒置关系,在面向对象设计中就是面向接口编程。因此,设计原则中的"依赖倒置原则"基本和"面向接口编程"含义一致。

[TODO]:后续补充面向对象设计、设计原则|设计模式之间的关系&异同点

二、应用实践

前面大概理解了依赖倒置原则的相关概念和含义之后,本节根据具体示例来体会下应用依赖倒置原则的优点。
以当下比较流行的直播带货场景为例,在这个场景中考虑两个模块:

  • 直播管理模块:负责管理直播间的创建、修改直播信息、查询搜索等功能
  • 商品供应模块:负责提供商品的信息和库存。

很明显,直播管理模块需要依赖商品供应模块提供的商品能力,因此这里前者就是上层模块,而后者就是下层模块。

2.1 直接依赖

在没有应用依赖倒置原则时,直播管理模块会直接依赖于具体的商品供应模块的实现。代码如下:
淘宝商品供应模块

public class TaobaoSupplier {
    public ProductInfo getProductInfo(String productId) {
        // 调用淘宝供应商的接口获取商品信息
        // ...
        return null;
    }

    public int getProductStock(String productId) {
        // 调用淘宝供应商的接口获取商品库存
        // ...
        return 0;
    }
}

② 直播管理模块-直接依赖淘宝商品供应模块

public class LiveRoomManager {
    private TaobaoSupplier supplier;

    public LiveRoomManager(TaobaoSupplier supplier) {
        this.supplier = supplier;
    }

    public void createLiveRoom(String roomId) {
        // 创建直播间的逻辑

        // 获取商品信息和库存
        ProductInfo productInfo = supplier.getProductInfo("1001");
        int productStock = supplier.getProductStock("1001");

        // 其他逻辑
    }
}

在这里插入图片描述
如上所示,直播管理模块直接依赖商品供应模块,这种强耦合关系满足最初业务是没有问题。如果之后业务扩展,底层商品供应渠道扩增,比如增加美团商家供应商、永辉超市供应等。那么这些属于商品供应链的改动也需要直播管理模块跟随改动。之前几篇文章也几乎都提到过,只要是变动,就会出现系统问题出现的风险,即系统稳定性变差。

2.2 应用“依赖倒置”原则

为了降低模块之间的耦合性,根据依赖倒置原则,我们新增抽象层,包括直播管理抽象类以及商品供应抽象类。因此,模块之间的依赖均建立在抽象类之间。对应的实现代码如下:
① 下层模块-抽象类

// 定义抽象的商品供应商接口
public interface Supplier {
    ProductInfo getProductInfo(String productId);
    int getProductStock(String productId);
}

② 下层模块-具体实现细节

// 实现具体的商品供应商类
public class TaobaoSupplier  implements Supplier  {

    public ProductInfo getProductInfo(String productId) {
        // 调用淘宝供应商的接口获取商品信息
        // ...
        return null;
    }

    public int getProductStock(String productId) {
        // 调用淘宝供应商的接口获取商品库存
        // ...
        return 0;
    }
}

③ 上层模块-抽象类

// 定义抽象的直播间管理接口
public abstract class LiveRoomManager {
    protected Supplier supplier;

    abstract void createLiveRoom(String roomId);
}

④ 上层模块-具体实现细节

// 实现具体的直播间管理类
public class DefaultLiveRoomManager extends LiveRoomManager {
    
    public DefaultLiveRoomManager (Supplier supplier) {
        this.supplier = supplier;
    }

    public void createLiveRoom(String roomId) {
        // 创建直播间的逻辑

        // 获取商品信息和库存
        ProductInfo productInfo = supplier.getProductInfo("1001");
        int productStock = supplier.getProductStock("1001");

        // 其他逻辑
    }
}

![在这里插入图片描述](https://img-blog.csdnimg.cn/6858d6f8ea674273a1757d723ba63a4b.pn
如上,当下层模块增加供应商时,上游无需改动,原因是Supplier抽象含义未发生变化。满足依赖倒置原则的好处就是降低了不同模块之间的耦合紧密度。上层模块仅需依赖下层抽象接口,不需要关心具体实现细节,自然也就不需要关注其中发生的变化,提高了系统的灵活性和可扩展性。
有个疑问,从类图中看上层具体实现类DefaultLiveRoomManager确实"直接"依赖了下层模块Supplier,那这个是否违背了依赖倒置的第一条直接依赖的规则?我认为这个不算直接依赖,从含义上讲,上层具体实现类(细节)是通过依赖其抽象,才依赖了下层模块,因此这不算直接依赖。

三、依赖

依赖倒置原则基本已经讲解清楚了,这个小节想写下大家可能会轻易忽略的模糊概念-依赖。依赖语言层面上是指依靠别人或事物而不能自立或自给,和代码世界中一样,生活着我们也是互相依赖、共同协作完成一系列重要任务。因此,依赖他人或他人的能力能够帮助我们解决很多事情,方便我们更加关注自身负责的事情。然而,在代码世界中,依赖似乎也是一把双刃剑,虽然能够通过封装、依赖使得系统结构清晰,但是依赖的不正当应用在面对复杂的业务变动中,会对系统的稳定性、可扩展性产生不好的影响。
不同代码模块之间或服务之间,我们常常会说强依赖、弱依赖,对于前者我们会谨慎对待,甚至对于返回的结果常常会添加多层数据校验逻辑以保证自身系统的稳定性。在面向对象中,类间的依赖关系可有以下几种:

  • 泛化:泛化描述的是类之间的继承关系。
  • 实现:实现指的是类实现接口之间所有功能。
  • 聚合:聚合表示整体与部分之间的关系,但是整体与部分不是强依赖的。即部分不存在时,整体依然可以是存在的。
  • 组合:组合也表示整体与部分之间的关系,但是整体与部分是强依赖的。即整体存在的前提是部分也必须存在。
  • 依赖:依赖表示实体之间在运行时会被依赖者会影响依赖者的行为。
  • 关联:关联表示不同类对象之间的关联性,这是一种静态

这几个关系中,重点关注下聚合、组合、依赖的区别。参考博客链接:https://www.cnblogs.com/jiqing9006/p/5915023.html

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

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

相关文章

missing-semester————2

文章目录 shell 脚本赋值语法函数逻辑运算符命令替换进程替换通配 shell工具查看命令如何使用查找文件查找代码查找shell指令 shell 脚本 很多情况下需要执行一系列的操作并使用条件或循环这样的控制流。 大多数shell都有自己的一套脚本语言,包括变量、控制流和自…

006-Logstash、FileBeat、ELK整合详解

目录 ELK架构背景需求架构logstash核心概念配置文件结构插件Codec Plugin-Multiline输出:elasticsearch输入:jdbcGrok插件Grok语法 mutate插件Date插件 Logstash Queue Beats配置步骤 ELK整合步骤1:日志采集步骤2:配置Logstash接收…

ChatGLM2体验+ubuntu18.04LTS+CPU版本

ChatGPT在自然语言处理领域的表现让人振奋,开启了大模型在通用人工智能领域的大门。 许多工作随之跟进,并开源,凭借相对小的参数量达到近似GPT的效果,包括LLama,alpace等。 其中LLama训练语料主要选择英语&#xff0…

Docker内部工作原理:容器化背后的魔法

Docker内部工作原理是怎样的? 现在我们知道了Docker是什么以及它提供了哪些好处,让我们逐个重要的细节来了解。 什么是容器?它们是如何工作的? 在深入研究Docker的内部机制之前,我们首先要了解容器的概念。简单地说&am…

在工作与生活中保持情绪稳定的艺术

强烈的情绪波动:工作中的挑战 在我的职业生涯中,我经历过许多情绪波动的时刻。其中一个最具挑战性的时刻是在我负责一个重要项目的时候。我需要在短时间内完成大量的工作,同时还要管理一个由不同背景和技能的人组成的团队。这个项目的压力让…

leetcode-704. 二分查找

leetcode-704. 二分查找 文章目录 leetcode-704. 二分查找一.题目描述二.第1次代码提交(非二分查找)三.第2次代码提交(非二分查找,std::find和std::distance)四.第3次代码提交(二分查找)五.关于C中int型的奇数除以2 一.题目描述 二.第1次代码提交(非二分查找) clas…

Openlayers实战:drawstart,drawend 绘制交互应用示例

Openlayers地图中,绘制一个多边形是非常见的一个应用,涉及到交互会在绘制开始 drawstart 和绘制结束drawend时,通常会在绘制完成后取消继续绘制,然后提出feature的一些信息。 效果图 源代码 /* * @Author: 大剑师兰特(xiaozhuanlan),还是大剑师兰特(CSDN) * @此源代…

B066-基础环境-前后端整合 批量删除 下拉 级联 增改

目录 批量删除页面调整普通属性的新增和修改引用属性的新增和修改管理员下拉列表部门树 见文档与代码 cd 子项目 运行前端项目 页面布局分析 批量删除 点击多选 - 改变data - 点击批量删除 - 带参数发请求 页面调整 略 普通属性的新增和修改 新增按钮:点击…

【MySQL系列】在Centos7环境安装MySQL

「前言」文章内容大致是在Centos7环境安装MySQL,演示安装的版本为5.7 「归属专栏」MySQL 「主页链接」个人主页 「笔者」枫叶先生(fy) 「枫叶先生有点文青病」「句子分享」 浮生梦,三生渺渺, 因缘无踪,虽堪恋,何必…

回归预测 | MATLAB实现CNN-BiGRU-Attention多输入单输出回归预测

回归预测 | MATLAB实现CNN-BiGRU-Attention多输入单输出回归预测 目录 回归预测 | MATLAB实现CNN-BiGRU-Attention多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现CNN-BiGRU-Attention多输入单输出回归预测,CNN-GRU结合…

Vision Transformer(VIT)论文解读及实现

1 论文解读 paper:VIT 1.1 VIT模型架构如下图所示: 图片原始输入维度 H * W * C在H和W按像素P切分,则H 、W可分割为 NPP, NHW/(PP),N为输入transform序列的长度。 x ∈ R H ∗ W ∗ C > x ∈ R N ∗ P 2 ∗ C x \in R^{H*W…

第三章 SSD存储介质:闪存 3.1

3.1 闪存物理结构 闪存芯片从小到大依此是由:cell(单元)、page(页)、block(块)、plane(平面)、die(核心)、NAND flash(闪存芯片&#…

Python find()函数使用详解

「作者主页」:士别三日wyx 「作者简介」:CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」:小白零基础《Python入门到精通》 find 1、指定检索位置2、参数为负数3、超出范围3、find()和index()的区别&#x…

【Docker】Docker安装MySQL

🚀欢迎来到本文🚀 🍉个人简介:陈童学哦,目前专攻C/C、Python、Java等方向,一个正在慢慢前行的普通人。 🏀系列专栏:陈童学的日记 💡其他专栏:CSTL、蓝桥杯&am…

Win11系统如何安装Oracle数据库(超级详细)

前言:在我们安装Oracle之前我们得理解Oracle数据库的优点是什么: Oracle是一个功能强大、可扩展和全面的数据库平台,具有广泛的功能和企业级能力,适用于处理复杂的企业级应用和大型数据集。 目录 一.下载Oracle数据库软件&…

解决idea只能通过 idea.bat打开的问题

解决:C盘用户下面 有idea的配置文件 ,找到idea64.exe.vmoptions 把 -jetbrain : 配置的 jar路径删除

Tablet vs. eReader: Which Is Better for Ebooks? 平板电脑与电子阅读器:哪个更适合电子书?

eReaders are best if all you want to do is have something as close to a paper book as possible. However, if you need anything more than that, a tablet makes more sense as a general-purpose device that can also read ebooks. 如果您只想拥有尽可能接近纸质书的东…

认识文件操作与IO

文章目录 认识文件文件夹文件路径文件分类 文件操作File类构造方法常用方法 字节流IOInputStream常用方法 FileInputStream构造方法FileInputStream实例 OutputStream方法 FileOutputStream 字符流IO 认识文件 我们平时所说的文件指的是存在硬盘上的文件,我们平时的…

Openlayers实战:回显多点、多线段、多多边形

Openlayers地图中,回显数据是非常重要的。 继上一示例回显点、线、圆形、多边形后。本示例回显多线,多点,多个多边形。用到了MultiPoint,MultiLineString,MultiPolygon。 多个信息的显示可以采用循环的方式,单个显示点、线、面。 但是循环方式是要多次计算的,而MultiPoint…

GUI (java)

GUI 一.GUI概念二.Swing概述三.容器组件四.常用容器1.窗体(1) JFrame类的构造方法(2) JFrame类的常用方法 2.面板(1)JPanel类的构造方法(2)JPanel类的常用方法 五.布局管理器1. FlowLayout 流式布局(1)FlowLayout构造方法 2.BorderLayout 边界布局3.GridLayout 网格布局 六.常用…