依赖倒转原则(Dependence Inversion Principle)
基本介绍:
-
高层模块(调用方)不应该依赖低层模块(被调用方),二者都应该依赖其抽象
-
抽象不应该依赖细节,细节应该依赖抽象
-
这意味着如果细节发生变化,它们不应该影响抽象。抽象是客户端查看对象的方式。确切地说,对象内部发生的事情并不重要。让我们以汽车为例,踏板,方向盘和变速杆是发动机内部发生的事情的抽象。他们不依赖于细节,因为如果有人改变我的旧引擎换新的引擎,我仍然可以在不知道发动机改变的情况下驾驶汽车。 另一方面,细节必须符合抽象所说的内容。我不想实现一个突然导致制动器使汽车速度加倍的发动机。我可以以任何我想要的方式重新制动刹车,只要从外部他们的行为方式相同。
-
依赖倒转(倒置)的中心思想是面向接口编程
-
依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在 java 中,抽象指的是接口或抽象类,细节就是具体的实现类
-
使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
-
依赖倒置原则是实现开闭原则的重要途径之一,它降低了客户与实现模块之间的耦合
依赖倒转原则的作用:
依赖倒置原则的主要作用如下:
- 依赖倒置原则可以降低类间的耦合性
- 依赖倒置原则可以提高系统的稳定性
- 依赖倒置原则可以减少并行开发引起的风险
- 依赖倒置原则可以提高代码的可读性和可维护性
应用实例:
请编程完成 Person 接收消息 的功能。
1) 实现方案1+分析说明
package com.atguigu.principle.inversion;
public class DependecyInversion {
public static void main(String[] args) {
Person person = new Person();
// person执行receive的过程对于调用方来说是抽象的,因为调用方不知道详细过程
// 而new Email()是具体实现的对象,抽象依赖实现就是这个意思
person.receive(new Email());
}
}
class Email {
public String getInfo() {
return "电子邮件信息: hello,world";
}
}
// 完成 Person 接收消息的功能
// 方式 1 分析
// 1. 简单,比较容易想到
// 2. 如果我们获取的对象是 微信,短信等等,则新增类,同时 Perons 也要增加相应的接收方法
// 3. 解决思路:引入一个抽象的接口 IReceiver,表示接收者,这样 Person 类与接口 IReceiver 发生依赖
// 因为 Email,WeiXin 等等属于接收的范围,他们各自实现 IReceiver 接口就 ok,这样我们就符合依赖倒转原则
class Person {
public void receive(Email email) {
System.out.println(email.getInfo());
}
}
2) 实现方案2(依赖倒转)+分析说明
package com.atguigu.principle.inversion.improve;
public class DependecyInversion {
public static void main(String[] args) {
// 客户端无需改变
Person person = new Person();
person.receive(new Email());
person.receive(new WeiXin());
}
}
// 定义接口
interface IReceiver {
public String getInfo();
}
class Email implements IReceiver {
public String getInfo() {
return "电子邮件信息: hello,world";
}
}
// 增加微信
class WeiXin implements IReceiver {
public String getInfo() {
return "微信信息: hello,ok";
}
}
// 方式 2
class Person {
//这里我们是对接口的依赖
public void receive(IReceiver receiver) {
System.out.println(receiver.getInfo());
}
}
依赖关系传递的三种方式
-
接口传递
-
构造方法传递
-
setter 方式传递
依赖倒转原则的注意事项和细节
-
低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好
-
变量的声明类型尽量是抽象类或接口, 这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化
-
继承时遵循里氏替换原则(里氏替换原则将在下篇文章讲解)