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

概述

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

相信有读过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{@Overridepublic void play() {System.out.println("长虹电视机打开了。");}
}
interface IOpenAndClose{public void open(ITV tv);
}
interface ITV{public void play();
}
//实现接口
class OpenAndClose implements IOpenAndClose{@Overridepublic 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{@Overridepublic 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;}@Overridepublic 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{@Overridepublic 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;@Overridepublic void setTv(ITV3 itv3) {this.itv3 = itv3;}@Overridepublic void open() {this.itv3.play();}
}

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于uniapp的 电子书小程序——需求整理

前言 想开发一个很简单的 电子书阅读小程序,要怎么做的。下面从功能、数据库设计这一块来说一下。说不一定能从某个角度提供一些思路 开发语言 springcloud uniapp 小程序(vue2)mysql 说明 电子书的主题是电子书,我们在日常…

GEE:基于 Landst 遥感数据计算的 kNDVI 下载 APP

作者:CSDN _养乐多_ 本文记录了在Google Earth Engine(GEE)平台中,使用 Landsat 遥感数据计算并且下载 kNDVI 的应用 APP 链接,并介绍该 APP 的使用方法和步骤。该APP可以为用户展示 NDVI 和 kNDVI 的遥感影像&#…

3ds Max 电脑配置建议 | 建模+渲染选专业显卡or游戏显卡?

使用3ds Max进行建模和渲染时,选择合适的电脑配置非常重要。比如在硬件选择上,究竟选购游戏显卡还是专业显卡呢?本文将为你详细介绍游戏显卡和专业显卡的区别,并提供配置建议,助你作出明智的决策。 &#…

NX二次开发UF_CSYS_set_wcs_display 函数介绍

文章作者:里海 来源网站:https://blog.csdn.net/WangPaiFeiXingYuan UF_CSYS_set_wcs_display Defined in: uf_csys.h int UF_CSYS_set_wcs_display(int display_status ) overview 概述 Set display of work coordinate system. 展示工作坐标系。 …

【数据分享】我国12.5米分辨率的DEM地形数据(免费获取/地理坐标系)

DEM地形数据是我们在各种研究和设计中经常使用的数据!之前我们分享过500米分辨率的DEM地形数据、90米分辨率的DEM地形数据、30米分辨率的DEM地形数据(均可查看之前的文章获悉详情)。 本次我们为大家带来的是分辨率为12.5m的DEM地形数据&#…

RK3568驱动指南|第七期-设备树-第69章 of操作函数实验:获取中断资源

瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工…

post请求参数全大写后台接不到参数

post请求参数全大写后台接不到参数 开发过程中,我们一般都习惯用驼峰命名法,但是特殊情况要求请求参数全大写(或者首字母大写),测试验证的时候发现,接收不到请求参数。 前端请求传递: 服务端接…

渗透测试信息搜集

注:太简陋了,不忍直视 渗透测试信息收集 黑盒测试:给域名 灰盒测试:给域名、账户(或密码) 白盒测试:给域名、账户、密码 授权书 对安全公司进行授权 攻防演习 是对个人进行授权 渗透测试:&#xff0…

AndroidStudio2022.3.1 Patch3使用国内下载源加速

记录一下这个版本的as在使用国内下载源加速碰到的诸多问题。 一、gradle-8.0-bin.zip下载慢 编辑项目文件夹/gradle/wrapper/gradle-wrapper.properties,文件内容改为如下: #Fri Nov 24 18:50:06 CST 2023 distributionBaseGRADLE_USER_HOME distribu…

生产环境出现问题,测试人如何做工作复盘?

很多时候我们能把大部分的Bug或一些部署等问题在业务上线之前就解决了,但由于某些因素,线上问题还是时而出现,影响业务生产甚至是公司效益。 避免线上问题的发生以及线上问题及时处理是测试人员的一项重要职责,如何快速地处理&am…

报错AttributeError: module ‘cv2‘ has no attribute ‘ximgproc‘

报错AttributeError: module ‘cv2’ has no attribute ‘ximgproc’ 首先查看是否安装opencv-contrib-python pip list | grep opencv显示 opencv-contrib-python 4.4.0.46 opencv-python 4.8.1.78 opencv-pyt…

SuperMap iDesktopX如何获取简单线的起终端点及坐标

作者:超图研究院技术支持中心-于丁 SuperMap iDesktopX如何获取简单线的起终端点及坐标 在GIS行业应用中,线数据的端点坐标有非常多的用处。 定位和可视化:线数据端点坐标可以用于定位和可视化线要素在空间中的位置。这对于地图制作、规划和…