查看“依赖倒置原则”的源代码
←
依赖倒置原则
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您所请求的操作仅限于该用户组的用户使用:
用户
您可以查看和复制此页面的源代码。
=== 依赖倒置原则的定义 === Dependence Inversion Principle 包含三层含义: * 高层模块不应该依赖低层模块,两者都应该依赖其抽象; * 抽象不应该依赖细节; * 细节应该依赖抽象。 高层模块和底层模块容易理解,每一个逻辑的实现都是由原子逻辑组成的,不可分割的原子逻辑就是低层模块,原子逻辑的再组装就是高层模块。 那什么是抽象?什么又是细节呢? 在 Java 语言中,抽象就是指接口或抽象类,两者都是不能直接被实例化的; 细节就是实现类,实现接口或继承抽象类而产生的类就是细节, 其特点就是可以直接被实例化,也就是可以加上一个关键字 new 产生一个对象。 依赖倒置原则在 Java 语言中的表现就是: * 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的; * 接口或抽象类不依赖于实现类; * 实现类依赖接口或抽象类。 更加精简的定义就是“面向接口编程”——OOD(Object-Oriented Design,面向对象设计)的精髓之一。 === 言而无信,你太需要契约 === 采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。 证明一个定理是否正确,有两种常用的方法: 一种是根据提出的论题,经过一番论证,推出和定理相同的结论,这是顺推证法; 还有一种是首先假设提出的命题是伪命题,然后推导出一个荒谬、与已知条件互斥的结论,这是反证法。 我们今天就用反证法来证明依赖倒置原则是多么优秀和伟大! 论题:依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。 反论题:不使用依赖倒置原则也可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。 我们通过一个例子来说明反论题是不成立的。 现在的汽车越来越便宜了,一个卫生间的造价就可以买到一辆不错的汽车,有汽车就必然有人来驾驶,死机驾驶奔驰车的类图如图所示: [[文件:司机驾驶奔驰车类图.png|无|缩略图|700x700像素]]奔驰车可以提供一个方法 run ,代表车辆运行,实现过程如代码:<syntaxhighlight lang="java"> /** * 司机源代码 */ public class Driver { public void drive(Benz benz) { benz.run(); } } </syntaxhighlight>司机通过调用奔驰车的 run 方法开动奔驰车,其源代码如:<syntaxhighlight lang="java"> /** * 奔驰车源代码 */ public class Benz { public void run() { System.out.println("奔驰汽车开始运行..."); } } </syntaxhighlight>有车,有司机,在 Client 场景类产生相应的对象,其源代码如:<syntaxhighlight lang="java"> /** * 场景类源代码 */ public class Client { public static void main(String[] args) { Driver zhangSan = new Driver(); Benz benz = new Benz(); //张三开奔驰车 zhangSan.drive(benz); } } </syntaxhighlight>通过以上的代码,完成了司机开动奔驰车的场景,到目前为止,这个司机开奔驰车的项目没有任何问题。 我们常说“危难时刻见真情”,我们把这句话移植到技术上就成了“变更才显真功夫”,业务需求变更永无休止,技术前进就永无止境,在发生变更时才能发觉我们的设计或程序是否是松耦合。 我们在一段貌似磐石的程序上加上一块小石头:张三司机不仅要开奔驰车,还要开宝马车,又该怎么实现呢? 麻烦出来了,那好,我们走一步是一步,我们先把宝马车产生出来,实现过程如下:<syntaxhighlight lang="java"> /** * 宝马车源代码 */ public class BMW { public void run() { System.out.println("宝马汽车开始运行..."); } } </syntaxhighlight>宝马车也产生了,但是我们却没有办法让张三开动起来,为什么? 张三没有开动宝马车的方法呀!一个拿有 C 驾照的司机竟然只能开奔驰车而不能开宝马车,这也太不合理了! 在现实世界都不允许存在这种情况,何况程序还是对现实世界的抽象,我们的设计出现了问题:司机类和奔驰车类之间是紧耦合的关系,其导致的结果就是系统的可维护性大大降低,可读性降低,两个相似的类需要阅读两个文件,你乐意吗? 还有稳定性,什么是稳定性?固化的、健壮的才是稳定的,这里只是增加了一个车类就需要修改司机类,这不是稳定性,这是易变性。 被依赖者的变更竟然让依赖者来承担修改的成本,这样的依赖关系谁肯承担!证明到这里,我们已经知道反论题已经部分不成立了。 '''<big>注意</big>''' '''设计是否具备稳定性,只要适当地“松松土”,观察“设计的蓝图”是否还可以茁壮地成长就可以得出结论,稳定性较高的设计,在周围环境频繁变化的时候,依然可以做到“我自岿然不动”。''' 我们继续证明,“减少并行开发引起的风险”,什么是并行开发的风险? 并行开发最大的风险就是风险扩散,本来只是一段程序的错误或异常,逐步波及一个功能,一个模块,甚至到最后毁坏了整个项目。 为什么并行开发就有这样的风险呢?一个团队,20个开发人员,各人负责不同的功能模块,甲负责汽车类的建造,乙负责司机类的建造,在甲没有完成的情况下,乙是不能完全地编写代码的,缺少汽车类,编译器根本就不会让你通过! 在缺少 Benz 类的情况下,Driver 类能编译吗?更不要说是单元测试了!在这种不使用依赖倒置原则的环境中,所有的开发工作都是“单线程”的,甲做完,乙再做,然后是丙继续……这在20世纪90年代“个人英雄主义”编程模式中还是比较适用的,一个人完成所有的代码工作。 但在现在的大中型项目中已经是完全不能胜任了,一个项目是一个团队协作的结果,一个“英雄”再牛也不可能了解所有的业务和所有的技术,要协作就要并行开发,要并行开发就要解决模块之间的项目依赖关系,那然后呢? 依赖倒置原则就隆重出场了! 根据以上证明,如果不使用依赖倒置原则就会加重类间的耦合性,降低系统的稳定性,增加并行开发引起的风险,降低代码的可读性和可维护性。 承接上面的例子,引入依赖倒置原则后的类图如图: [[文件:引入依赖倒置原则后的类图.png|无|缩略图|550x550像素]] 建立两个接口:IDriver 和 ICar,分别定义了司机和汽车的各个职能,司机就是驾驶汽车,必须实现 drive() 方法,其实现过程如代码:<syntaxhighlight lang="java"> /** * 司机接口 */ public interface IDriver { //是司机就应该会驾驶汽车 public void drive(ICar car); } </syntaxhighlight>接口只是一个抽象化的概念,是对一类事物的最抽象描述,具体的实现代码由相应的实现类来完成,Driver 实现类如代码:<syntaxhighlight lang="java"> /** * 司机类的实现 */ public class Driver implements IDriver { //司机的主要职责就是驾驶汽车 @Override public void drive(ICar car) { car.run(); } } </syntaxhighlight>
返回至
依赖倒置原则
。
导航菜单
个人工具
登录
名字空间
页面
讨论
变种
视图
阅读
查看源代码
查看历史
更多
搜索
导航
首页
Spring Boot 2 零基础入门
Spring Cloud
Spring Boot
设计模式之禅
VUE
Vuex
Maven
算法
技能树
Wireshark
IntelliJ IDEA
ElasticSearch
VirtualBox
软考
正则表达式
程序员精讲
软件设计师精讲
初级程序员 历年真题
C
SQL
Java
FFmpeg
Redis
Kafka
MySQL
Spring
Docker
JMeter
Apache
Linux
Windows
Git
ZooKeeper
设计模式
Python
MyBatis
软件
数学
PHP
IntelliJ IDEA
CS基础知识
网络
项目
未分类
MediaWiki
镜像
问题
健身
国债
英语
烹饪
常见术语
MediaWiki帮助
工具
链入页面
相关更改
特殊页面
页面信息