03.依赖倒置原则(Dependence Inversion Principle)

news2024/11/15 21:41:08

概述

高层模块不应依赖低层模块,二者都应该依赖其抽象。而抽象不应依赖细节,细节应该依赖抽象。依赖倒置原则的中心思想其实就是面向接口编程
相对于细节的多变性,抽象的东西会稳定的多,所以以抽象为基础搭建的架构自然也会比以细节为基础搭建的架构稳定的多
使用接口或抽象类的目的是为了更好的制定规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。

相信有读过spring framework源码的同学应该对这一点深以为是,比如其核心接口之一的BeanFactory接口之下就继承了AutowireCapableBeanFactory、HierarchicalBeanFactory、ListableBeanFactory接口,而这些接口又被多个抽象类有选择实现,正是对依赖倒置原则的应用才使得spring framework 框架具有了极高的健壮性和扩展性。
在这里插入图片描述

三寸反骨

我们不妨从相反的角度出发,写个反例看看会有什么问题,今日反骨颇重,就是不想遵循依赖倒置原则。
比如我们现在需要编写一个简单的Person类,让他可以接受邮件消息即可。
反例

public class DependecyInversion {
    public static void main(String[] args) {
        Person person = new Person();
        person.receive(new Email());
    }
}

class Email{
    public String getInfo(){
        return "电子邮件信息: Hello,Email";
    }
}

class Person {
    public void receive(Email email){
        System.out.println(email.getInfo());
    }
}

编程的过程十分愉快,如此简单的需求甚至不需要聪明的我们多思考一秒。运行效果也完全满足预期。
但是,来了一个不好不坏的消息:“能力需要扩展,客户要求,除了收到email的能力外,微信消息也想收到!”
“反骨仔想了想,不要紧,我新增类,同时Person类也要增加相应的方法就好了呀”!
于是他加班1小时完成了这个需求,自信满满的走了。
但是,第二天又来了个不好不坏的消息:“客户对产品很满意,同时要求除了想收到email、微信消息之外,还想扩展几十个软件的消息,清单包含:“QQ、微博、墨迹天气、钉钉…”
反骨仔爆炸了,因为这个需求如果继续按他的思路去实现,类也爆炸了。实现的方法更是爆炸到难以维护。
所以说一定要设计先行,一个优秀的设计可能会占据相当长的开发时间,但是会为后期的扩展和维护提供强有力的保障!


优化设计

我们把时间推回两天前,接到需求的我们首先便进行了深度剖析并达成了共识:“依赖倒置原则必须遵守!”于是,有了如下代码:

public class DependecyInversion {
    public static void main(String[] args) {
        Person person = new Person();
        person.receive(new Email());
        person.receive(new WeChat());
    }
}
interface IReceive{
    public String getInfo();
}

class Email implements IReceive{
    public String getInfo(){
        return "电子邮件信息: Hello,Email";
    }
}

class WeChat implements IReceive{
    public String getInfo(){
        return "微信信息: Hello,WeChat";
    }
}

class Person {
    public void receive(IReceive receiver){
        System.out.println(receiver.getInfo());
    }
}

当客户说我要继续扩展几十个消息入口时,我们会发现,我们对于person类不需要做任何改动了,不过是需要遵照IReceive接口规范去实现新扩展的业务细节就可以了。善莫大焉。


依赖关系传递三板斧

常见的依赖关系传递有三种方式:

接口传递

public class DependencyPass {
    public static void main(String[] args) {
        ChangHong changHongTv = new ChangHong();
        OpenAndClose openAndClose = new OpenAndClose();
        openAndClose.open(changHongTv);
    }
}

class ChangHong implements ITV,ITV2,ITV3{
    @Override
    public void play() {
        System.out.println("长虹电视机打开了。");
    }
}
interface IOpenAndClose{
    public void open(ITV tv);
}
interface ITV{
    public void play();
}
//实现接口
class OpenAndClose implements IOpenAndClose{
    @Override
    public void open(ITV tv) {
        tv.play();
    }
}

构造方法传递

public class DependencyPass {
    public static void main(String[] args) {
        ChangHong changHongTv = new ChangHong();
        OpenAndClose2 openAndClose2 = new OpenAndClose2(changHongTv);
        openAndClose2.open();
    }

}
class ChangHong implements ITV,ITV2,ITV3{
    @Override
    public void play() {
        System.out.println("长虹电视机打开了。");
    }
}
interface IOpenAndClose2{
    public void open();
}
interface  ITV2{
    public void play();
}
class OpenAndClose2 implements IOpenAndClose2{
    public ITV2 tv;
    public OpenAndClose2(ITV2 tv){
        this.tv = tv;
    }
    @Override
    public void open() {
        this.tv.play();
    }
}

setter方法传递

public class DependencyPass {
    public static void main(String[] args) {
        ChangHong changHongTv = new ChangHong();
        OpenAndClose3 openAndClose3 = new OpenAndClose3();
        openAndClose3.setTv(changHongTv);
        openAndClose3.open();
    }

}

class ChangHong implements ITV,ITV2,ITV3{
    @Override
    public void play() {
        System.out.println("长虹电视机打开了。");
    }
}
interface IOpenAndClose3{
    public void open();
    public void setTv(ITV3 itv3);
}
interface ITV3{
    public void play();
}
class OpenAndClose3 implements IOpenAndClose3{
    private ITV3 itv3;
    @Override
    public void setTv(ITV3 itv3) {
        this.itv3 = itv3;
    }
    @Override
    public void open() {
        this.itv3.play();
    }
}

  1. 底层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好;
  2. 变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间就存在一个缓冲层,利于程序扩展和优化;
  3. 继承时遵循里氏替换原则;
  4. 什么是里氏替换原则?下次讲!

关注我,共同进步,每周至少一更。——Wayne

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

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

相关文章

【Tiny_CD】Tiny_CD变化检测网络详解(含python代码)

题目:TinyCD: A (Not So) Deep Learning Model For Change Detection 论文:paper 代码:code 目录 🍟 🍟1.摘要 🍗🍗 2.贡献 🍖🍖 3.网络结构

语音合成综述Speech Synthesis

一、语音合成概述 语音信号的产生分为两个阶段,信息编码和生理控制。首先在大脑中出现某种想要表达的想法,然后由大脑将其编码为具体的语言文字序列,及语音中可能存在的强调、重读等韵律信息。经过语言的组织,大脑通过控制发音器…

python中pprint()与print()

平常经常使用print(),今天突然看到pprint(),好奇查了下,记录。 统而言之,pprint()更适合打印结构化数据,对于可读性有很大的提升 这里有两个对象在一个arry里面,普通的print直接全部打印出来,而…

贝锐花生壳:无需公网IP、简单3步,远程访问群晖NAS

面对NAS远程访问难题,贝锐花生壳一招搞定!并且无需公网IP、简单3步,即可实现固定域名远程访问NAS。 步骤1: 目前,群晖NAS已在套件中心内置花生壳客户端。 浏览器进入群晖NAS的DSM管理界面,点击【套件中心】…

机器学习算法(1)——简单线性回归

一、说明 在在这篇文章中,我们将学习我们的第一个机器学习算法,称为简单线性回归。这是一个重要的算法,因为当您可能正在学习第一个神经网络(称为人工神经网络)时,在此算法中学习的技术也适用于深度学习。我…

OpenStack云计算平台

目录 一、OpenStack 1、简介 2、硬件需求 3、网络 二、环境搭建 1、安全 2、主机网络 3、网络时间协议(NTP) 4、OpenStack包 5、SQL数据库 6、消息队列 7、Memcached 一、OpenStack 1、简介 官网:https://docs.openstack.org/2023.2/ OpenStack系统由…

简单使用YOLOv5自己训练模型

使用YOLOv5自己训练模型 前言:本文基于我的另一篇文章作为基础,文章戳这里,主要还是实操为主,让大家能快速上手使用。 数据集构建 1.准备工作 数据收集: 图片类型数据 视频类型数据(使用opencv进行视频…

矩阵论(Matrix)

​ 大纲 矩阵微积分:多元微积分的一种特殊表达,尤其是在矩阵空间上进行讨论的时候逆矩阵(inverse matrix)矩阵分解:特征分解(Eigendecomposition),又称谱分解(Spectral decomposition&#xf…

【MATLAB源码-第88期】基于matlab的灰狼优化算法(GWO)的栅格路径规划,输出做短路径图和适应度曲线

操作环境: MATLAB 2022a 1、算法描述 灰狼优化算法(Grey Wolf Optimizer, GWO)是一种模仿灰狼捕食行为的优化算法。灰狼是群居动物,有着严格的社会等级结构。在灰狼群体中,通常有三个等级:首领&#xff…

java中 自动装箱与拆箱,基本数据类型,java堆与栈,面向对象与面向过程

文章目录 自动装箱与拆箱基本数据类型与包装类的区别(int 和 Integer 有什么区别)应用场景的区别: 堆和栈的区别重点来说一下堆和栈:那么堆和栈是怎么联系起来的呢? 堆与栈的区别 很明显:延伸:关于Integer…

python opencv 边缘检测(sobel、沙尔算子、拉普拉斯算子、Canny)

python opencv 边缘检测(sobel、沙尔算子、拉普拉斯算子、Canny) 这次实验,我们分别使用opencv 的 sobel算子、沙尔算子、拉普拉斯算子三种算子取进行边缘检测,然后后面又使用了Canny算法进行边缘检测。 直接看代码,代…

【腾讯云云上实验室-向量数据库】基于向量数据的客户价值体系推荐系统设计

【腾讯云云上实验室-向量数据库】深入浅出-基于向量分析的客户价值体系推荐系统设计 前言 很早之前就有过想写推荐系统系列文章了,本人曾任职高级大数据工程师全程参与过推荐系统的搭建,故在搭建推荐系统算得上是有一定的经验。推荐系统搭建有相当多的…

UI自动化(selenium+python)之元素定位的三种等待方式!

前言 在UI自动化过程中,常遇到元素未找到,代码报错的情况。这种情况下,需要用等待wait。 在selenium中可以用到三种等待方式即sleep,implicitly_wait,WebDriverWait 一、固定等待(sleep) 导入time模块,设定固定的等待时间 缺…

【点云surface】无序点云快速三角化

1 介绍 GreedyProjectionTriangulation 是一种基于局部二维投影的三维点贪婪三角剖分算法的实现。它假定局部表面光滑,不同点密度区域之间的过渡相对平滑。 GreedyProjectionTriangulation算法的基本思想是通过逐步投影点云数据到一个三角化网格上来进行重建。它首…

Pycharm创建项目新环境,安装Pytorch

在python项目中,很多项目使用的各类包的版本是不一致的。所以我们可以对每个项目有专属于它的环境。所以这个文章就是教你如何创建新环境。 一、创建新环境 二、下载安装包 在下载安装包时,可以加入清华源,这样下载更快!不然有时…

消息推送到微信,快速实现WxPusher

文章目录 前言一、平台二、代码总结 前言 我的博客里也有其他方法,测试了下感觉这个方法还是比较实用。 一、平台 先仔细阅读下平台的使用方法。 平台地址请点击 二、代码 import requests text 孪生网络模型已经训练完成,请注意查阅相关信息。 req…

学习Pandas 二(Pandas缺失值处理、数据离散化、合并、交叉表与透视表、分组与聚合)

文章目录 六、高级处理-缺失值处理6.1 检查是否有缺失值6.2 缺失值处理6.3 不是缺失值NaN,有默认标记的 七、高级处理-数据离散化7.1 什么是数据的离散化7.2 为什么要离散化7.3 如何实现数据的离散化 八、高级处理-合并8.1 pc.concat实现合并,按方向进行…

Linux文件基础(文件查看及vim)

文件查看命令: (1)cat 1)查看文件内容(内容较少时使用):cat 文件名 2)合并文件:cat 文件名1 文件名2> 文件名3 3)往文件中写入数据,(Ctrld结束输入); (2)more more 文件名 文件内容较多时用more(空格,回车往下翻,b回滚) (3)less less 文件名 看完内容之后不会显示…

最重要的BI测试-适用于任何BI和分析平台

为什么 BI 测试是答案 相信你的数据可视化是成功执行商业智能 (BI) 和分析项目的关键因素。我敢肯定,你遇到过以下情况:业务主管或业务用户反馈说他们的分析看起来不对,他们的 KPI 看起来有问题,或者速度太慢而无法使用。要问自己…

【Spring篇】JDK动态代理

目录 什么是代理? 代理模式 动态代理 Java中常用的代理模式 问题来了,如何动态生成代理类? 动态代理底层实现 什么是代理? 顾名思义,代替某个对象去处理一些问题,谓之代理,那么何为动态&a…