当前位置: 首页 > news >正文

【23种设计模式】依赖倒置原则

个人主页:金鳞踏雨

个人简介:大家好,我是金鳞,一个初出茅庐的Java小白

目前状况:22届普通本科毕业生,几经波折了,现在任职于一家国内大型知名日化公司,从事Java开发工作

我的博客:这里是CSDN,是我学习技术,总结知识的地方。希望和各位大佬交流,共同进步 ~

要依赖于抽象而不是具体实现

依赖倒置的目的是,低层模块可以随时替换,以提高代码的可扩展性。

一、原理

要依赖于抽象而不是具体实现。遵循这个原则可以使系统的设计更加灵活、可扩展和可维护

  1. 高层模块不应该依赖于低层模块,它们都应该依赖于抽象。
  2. 抽象不应该依赖于具体实现,具体实现应该依赖于抽象。

倒置在这里的确是指"反过来"的意思。在依赖倒置原则中,我们需要改变依赖关系的方向,使得高层模块和低层模块都依赖于抽象,而不是高层模块直接依赖于低层模块。这样一来,依赖关系就从直接依赖具体实现"反过来"依赖抽象了

这种"倒置"的依赖关系使得系统的耦合度降低,提高了系统的可维护性和可扩展性。因为,低层模块的具体实现发生变化时,只要不改变抽象,高层模块就不需要进行调整所以这个原则叫做依赖倒置原则。

二、如何理解抽象

当我们在讨论依赖倒置原则中的抽象时,绝对不能仅仅把他理解为一个接口。抽象的目的是将关注点从具体实现转移到概念和行为,使得我们在设计和编写代码时能够更加关注问题的本质。通过使用抽象,我们可以创建更加灵活、可扩展和可维护的系统。

事实上抽象是一个很广泛的概念,它可以包括接口、抽象类以及由大量接口,抽象类和实现组成的更高层次的模块。通过将系统分解为更小的、可复用的组件,我们可以实现更高层次的抽象。这些组件可以独立地进行替换和扩展,从而使整个系统更加灵活。

1. 接口

接口是 Java 中实现抽象的一种常见方式。接口定义了一组方法签名,表示实现该接口的类应具备哪些行为。接口本身并不包含具体实现,所以它强调了行为的抽象。

假设我们正在开发一个在线购物系统,其中有一个订单处理模块。订单处理模块需要与不同的支付服务提供商(如 PayPal、Stripe 等)进行交互。如果我们直接依赖于支付服务提供商的具体实现,那么在更换支付服务提供商或添加新的支付服务提供商时,我们可能需要对订单处理模块进行大量修改。为了避免这种情况,我们应该依赖于接口而不是具体实现。

首先,我们定义一个支付服务接口

public interface PaymentService {boolean processPayment(Order order);
}

然后,为每个支付服务提供商实现该接口

public class PayPalPaymentService implements PaymentService {@Overridepublic boolean processPayment(Order order) {// 实现 PayPal 支付逻辑}
}public class StripePaymentService implements PaymentService {@Overridepublic boolean processPayment(Order order) {// 实现 Stripe 支付逻辑}
}

现在,我们可以在订单处理模块中依赖 PaymentService 接口,而不是具体的实现:

public class OrderProcessor {private PaymentService paymentService;public OrderProcessor(PaymentService paymentService) {this.paymentService = paymentService;}public void processOrder(Order order) {// 其他订单处理逻辑...boolean paymentResult = paymentService.processPayment(order);// 根据 paymentResult 处理支付结果}
}

通过这种方式,当我们需要更换支付服务提供商或添加新的支付服务提供商时,只需要提供一个新的实现类,而不需要修改 OrderProcessor 类。我们可以在运行时通过构造函数注入不同的支付服务实现,使得系统更加灵活和可扩展。

2. 抽象类

抽象类是另一种实现抽象的方式。与接口类似,抽象类也可以定义抽象方法,表示子类应该具备哪些行为。不过抽象类还可以包含部分具体实现,这使得它们比接口更加灵活

abstract class Shape {abstract double getArea();void displayArea() {System.out.println("面积为: " + getArea());}
}class Circle extends Shape {private final double radius;Circle(double radius) {this.radius = radius;}@Overridedouble getArea() {return Math.PI * Math.pow(radius, 2);}
}class Square extends Shape {private final double side;Square(double side) {this.side = side;}@Overridedouble getArea() {return Math.pow(side, 2);}
}

在这个示例中,我们定义了一个抽象类 Shape,它具有一个抽象方法 getArea,用于计算形状的面积。同时,它还包含了一个具体方法 displayArea,用于打印面积。

Circle 和 Square 类继承了 Shape,分别实现了 getArea 方法。在其他类中我们可以依赖抽象Shape而非 Square和Circle。

三、如何理解高层模块和底层模块

所谓高层模块低层模块的划分,简单来说就是,在调用链上,调用者属于高层,被调用者属于低层。在平时的业务代码开发中,高层模块依赖底层模块是没有任何问题的。实际上,这条原则主要还是用来指导框架层面的设计,跟前面讲到的控制反转类似。

用 Tomcat 这个 Servlet 容器作为例子来解释一下。从业务代码上讲,举一个简单的例子就是controller要依赖service的接口而不是实现,service实现要依赖dao层的接口而不是实现,调用者要依赖被调用者的接口而不是实现

以一个简单的音频播放器为例,高层模块 AudioPlayer 负责播放音频,而音频文件的解码由低层模块 Decoder 实现。为了遵循依赖倒置原则,我们可以引入一个抽象的解码器接口:

interface AudioDecoder {AudioData decode(String filePath);
}class AudioPlayer {private final AudioDecoder decoder;public AudioPlayer(AudioDecoder decoder) {this.decoder = decoder;}public void play(String filePath) {AudioData audioData = decoder.decode(filePath);// 使用解码后的音频数据进行播放}
}class MP3Decoder implements AudioDecoder {@Overridepublic AudioData decode(String filePath) {// 实现 MP3 文件解码}
}

在这个例子中,我们将高层模块 AudioPlayer 和低层模块 MP3Decoder 解耦,使它们都依赖于抽象接口 AudioDecoder。这样,我们可以根据需要轻松地更换音频解码器(例如,支持不同的音频格式),而不影响音频播放器的逻辑。为了支持新的音频格式,我们只需要实现新的解码器类,并将其传递给 AudioPlayer。

假设我们现在要支持 WAV 格式的音频文件,我们可以创建一个实现 AudioDecoder 接口的新类:

class WAVDecoder implements AudioDecoder {@Overridepublic AudioData decode(String filePath) {// 实现 WAV 文件解码}
}

然后,在创建 AudioPlayer 对象时,我们可以根据需要选择使用 MP3Decoder 或 WAVDecoder:

public static void main(String[] args) {AudioDecoder mp3Decoder = new MP3Decoder();AudioPlayer mp3Player = new AudioPlayer(mp3Decoder);mp3Player.play("example.mp3");AudioDecoder wavDecoder = new WAVDecoder();AudioPlayer wavPlayer = new AudioPlayer(wavDecoder);wavPlayer.play("example.wav");
}

通过遵循依赖倒置原则,我们将高层模块 AudioPlayer 与低层模块 MP3Decoder 和 WAVDecoder 解耦,使它们都依赖于抽象接口 AudioDecoder。这样的设计使得我们可以轻松地为音频播放器添加新的音频格式支持,同时保持整个系统的灵活性和可维护性。

Tomcat

Tomcat 是运行 Java Web 应用程序的容器。我们编写的 Web 应用程序代码只需要部署在 Tomcat 容器下,便可以被 Tomcat 容器调用执行。

按照之前的划分原则,Tomcat 就是高层模块,我们编写的 Web 应用程序代码就是低层模块

Tomcat 和应用程序代码之间并没有直接的依赖关系,两者都依赖同一个“抽象”,也就是 Sevlet 规范。Servlet 规范不依赖具体的 Tomcat 容器和应用程序的实现细节,而 Tomcat 容器和应用程序依赖 Servlet 规范。这样做的好处就是tomcat中可以运行任何实现了servlet规范的应用程序,同时我们编写的servlet实现(web)工程也可以运行在不同的web服务器中。

四、IOC容器

依赖倒置的目的是,低层模块可以随时替换,以提高代码的可扩展性。

其实我们学过spring的同学应该都清楚,在spring中实现这个很简单的,我们只需要向容器中注入特定的bean就能切换具体实现。同时我们在编写日常代码时,有意无意的都会遵循设计原则

控制反转是一种软件设计原则,它将传统的控制流程颠倒过来,将控制权交给一个中心化的容器或框架

依赖注入是指不通过 new() 的方式在类内部创建依赖类对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类使用。

通过控制翻转依赖注入结合,我们只要保证依赖抽象而不是实现,就能很轻松的替换实现。如给容器注入一个MySQL的数据,则所有依赖数据源的部分会自动使用MySQL,如果想替换数据源则仅仅需要给容器注入一个新的数据源就好了,不需要修改一行代码。

文章到这里就结束了,如果有什么疑问的地方,可以在评论区指出~

希望能和大佬们一起努力,诸君顶峰相见

再次感谢各位小伙伴儿们的支持!!!

相关文章:

【23种设计模式】依赖倒置原则

个人主页:金鳞踏雨 个人简介:大家好,我是金鳞,一个初出茅庐的Java小白 目前状况:22届普通本科毕业生,几经波折了,现在任职于一家国内大型知名日化公司,从事Java开发工作 我的博客&am…...

C++ 结构简介

假设要存储有关篮球运动员的信息,则可能需要存储他(她)的姓名、工资、身高、体重、平均得 分、命中率、助攻次数等。希望有一种数据格式可以将所有这些信息仔储在一个单元中。数组不能完成 这项任务,因为虽然数组可以存储多个元素,但所有元素的类型必须相同。也就是说,一个数组…...

element的tabs组件使用问题解决

1.去除el-tabs组件自带的键盘切换功能 今天在使用element的tabs组件时&#xff0c;发现这个tab组件自带了按键盘左右方向&#xff0c;切换tab的功能&#xff0c;可以通过使用keydown.native.capture.stop去除该事件 <el-tabs v-model"editableTabsValue"type&qu…...

python实验1 猜数字游戏

实验0&#xff1a;猜数字游戏 1. 猜数字游戏 版本12. 猜数字游戏 版本23. 猜数字游戏 版本34. 猜数字游戏 版本4 1. 猜数字游戏 版本1 题目猜数字游戏。在程序中预设一个0-9之间的整数, 让用户通过键盘输入所猜的数&#xff0c; 如果大于预设的数,显示“你猜的数字大于正确答案…...

docker 中给命令起别名

docker 的有些命令特别复杂&#xff0c;我们可以给它设置别名简化输入&#xff0c;就不用每次都输入那么多了&#xff01;&#xff01;&#xff01; 1. 进入 .bashrc 中修改配置&#xff08; .bashrc 是root下的隐藏文件&#xff09; cd /rootvim .bashrc2. 在 .bashrc 中加入…...

PHP的yaf框架自带插件

Yaf 框架的插件方法触发流程遵循一定的顺序&#xff0c;具体流程如下 Bootstrap 类的 _initPlugin 方法&#xff1a;在 Yaf 应用程序启动时&#xff0c;首先会执行 Bootstrap 类的 _initPlugin 方法。在这个方法中&#xff0c;你可以注册各种插件。例如&#xff1a; phpCopy …...

SpringCloud Alibaba【三】Gateway

Gateway配置与使用 前言新建gateway子项目pom.xml配置文件启动类访问接口方式 测试拓展 前言 在工作中遇到一种情况&#xff0c;一个父项目中有两个子项目。实际使用时&#xff0c;需要外网可以访问&#xff0c;宝信软件只能将一个端口号发布在外网上&#xff0c;所以需要运用…...

Azure - 机器学习实战:快速训练、部署模型

本文将指导你探索 Azure 机器学习服务的主要功能。在这里&#xff0c;你将学习如何创建、注册并发布模型。此教程旨在让你深入了解 Azure 机器学习的基础知识和常用操作。 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验…...

C语言十进制转其它进制

短除法介绍 短除法&#xff1a; 主要功能为将十进制数据转为其它进制的数据&#xff0c;假设我们要转换为 X 进制&#xff0c;那么具体的流程如下&#xff1a; 十进制数字不断除以 X&#xff0c;直到商为 0 记录每次计算得到的余数 将余数倒序输出&#xff0c;即为对应的 X 进…...

网络建设 之 React数据管理

React作为一个用于构建用户界面的JavaScript库&#xff0c;很多人认为React仅仅只是一个UI 库&#xff0c;而不是一个前端框架&#xff0c;因为它在数据管理上是缺失的。在做一个小项目的时候&#xff0c;维护的数据量不多&#xff0c;管理/维护数据用useState/useRef就足够了&…...

如何隐藏woocommerce 后台header,woocommerce-layout__header

如何隐藏woocommerce 后台header&#xff0c;woocommerce-layout__header WooCommerce |Products Store Activity| Inbox| Orders| Stock| Reviews| Notices| breadcrumbs 在 functions.php 里添加如下代码即可&#xff1a; // Disable WooCommerce Header in WordPress Admi…...

通俗易懂的理解 解耦 概念

解耦&#xff08;Decoupling&#xff09;是计算机科学和软件工程中的一个概念&#xff0c;指的是降低系统中不同部分之间的依赖性&#xff0c;使系统的各个组件能够相对独立地进行开发、维护和演化。解耦的主要目标是减少组件之间的紧密耦合&#xff0c;以提高系统的灵活性、可…...

全志A40i android7.1 增加Vlan功能

一&#xff0c;VLAN基础知识 1.VLAN的定义&#xff1a; VLAN&#xff08;Virtual Local Area Network&#xff09;即虚拟局域网&#xff0c;是将一个物理的LAN在逻辑上划分成多个广播域的通信技术。VLAN内的主机间可以直接通信&#xff0c;而VLAN间不能直接通信&#xff0c;从…...

NAT技术与代理服务器

目录 一、NAT与NAPT技术 1.NAT技术 2.NAPT技术 &#xff08;1&#xff09;四元组的唯一性 &#xff08;2&#xff09;数据的传输过程 &#xff08;3&#xff09;NAPT的缺陷 二、代理服务器 1.正向代理和反向代理 2.代理服务器的应用 &#xff08;1&#xff09;游戏加…...

关于报错java.util.ConcurrentModificationException: null的源码分析和解决

一般有这种问题,方法中至少会有List或者Map下的至少两个子类,有可能参数类型相同,也有可能不同都有可能触发这个问题!其主要原因是使用了ArrayList进行删除操作或者使用iterator遍历集合的同时对集合进行修改都有可能会出现这个问题 ArrayList属于List下的子类 需要区分的是Li…...

使用koa搭建服务器(一)

最近有个需求需要使用到koa搭建服务器并编写接口对数据库进行增删改查&#xff0c;因此写一篇博客记录这段时间的收获。 一、新建koa项目 &#xff08;一&#xff09;安装koa及其相关依赖 npm i koa npm i koa-router// 中间件&#xff0c;用于匹配路由 npm i koa-bodyparse…...

echarts的柱状图的重叠和堆叠实现两个柱体的显示和之前的差值显示

效果图 主要思路 准备三个柱体&#xff08;原计划&#xff0c;实际进度&#xff0c;差值&#xff09; 原计划和实际进度设置成重叠 {barWidth: 20,// yAxisIndex: 1,z: 1,name: 原计划,type: bar,stack: ab,emphasis: { // 点击柱体其他柱体颜色会变浅disabled: true},label…...

泛积木-低代码 使用攻略

文档首发于 泛积木-低代码 使用攻略 我们以大纲的方式&#xff08;总体把握&#xff09;讲述如何高效、便捷使用 泛积木-低代码。 权限 首先说下权限&#xff0c;在 系统设置 / 权限设置 菜单内&#xff0c;我们可以新增调整项目内的权限&#xff0c;默认拥有管理员和成员两…...

红队专题-从零开始VC++C/S远程控制软件RAT-MFC-远控介绍及界面编写

红队专题 招募六边形战士队员[1]远控介绍及界面编写1.远程控制软件演示及教程简要说明主程序可执行程序 服务端生成器主机上线服务端程序 和 服务文件管理CMD进程服务自启动主程序主对话框操作菜单列表框配置信息 多线程操作非模式对话框 2.环境&#xff1a;3.界面编程新建项目…...

机器学习(五)如何理解机器学习三要素

1.8如何理解机器学习三要素 统计学习模型策略算法 模型&#xff1a;规律yaxb 策略&#xff1a;什么样的模型是好的模型&#xff1f;损失函数 算法&#xff1a;如何高效找到最优参数&#xff0c;模型中的参数a和b 1.8.1模型 机器学习中&#xff0c;首先要考虑学习什么样的…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...