依赖倒转原则(DIP): 高层模块不应该依赖低层模块,两者都应该依赖其抽象抽象不应该依赖细节,细节应该依赖抽象。
依赖倒置原则在Java语言中的表现:
模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的 接口或抽象类不依赖于实现类 实现类依赖于接口或抽象类
更为精简的定义: 面向接口编程(Object-Oriented Design, OOD)
DIP的好处: 采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。
DIP的几种写法
接口声明依赖对象: 在接口的方法中声明依赖对象。 构造函数传递依赖对象: 在类中通过构造函数声明依赖对象(好比Spring中的构造器注入),采用构造器注入。 Setter方法传递依赖对象: 在抽象中设置Setter方法声明依赖对象(Spring中的方法注入)。
依赖倒置原则的本质: 就是通过抽象(抽象类或接口)使各个类或模块实现彼此独立,不互相影响,实现模块间的松耦合。
在项目中使用这个规则需要以下原则;
每个类尽量都要有接口或抽象类,或者抽象类和接口都有: 依赖倒置原则的基本要求,有抽象才能依赖倒置。 变量的表面类型尽量是接口或者抽象类。 任何类都不应该从具体类派生。 尽量不要重写基类已经写好的方法(里式替换原则)。 结合里式替换原则来使用: 结合里式替换原则和依赖倒置原则我们可以得出一个通俗的规则, 接口负责定义public属性和方法,并且声明与其他对象的依赖关系,抽象类负责公共构造部分的实现,实现类准确的实现业务逻辑, 同时在适当的时候对父类进行细化。
一句话:依赖倒置原则的核心就是面向抽象(抽象类或者接口)编程
案例1:
未遵守依赖倒转原则
组装电脑:
现要组装一台电脑,需要配件cpu,硬盘,内存条。只有这些配置都有了,计算机才能正常的运行。选择cpu有很多选择,如Intel,AMD等,硬盘可以选择希捷,西数等,内存条可以选择金士顿,海盗船等。
类图
希捷硬盘类
public class XiJieHardDisk {
//存储数据的方法
public void save(String data) {
System.out.println("使用希捷硬盘存储数据为:" + data);
}
//获取数据的方法
public String get() {
System.out.println("使用希捷硬盘取数据");
return "数据";
}
}
金士顿内存条类
public class KingstonMemory {
public void save() {
System.out.println("使用金士顿内存条");
}
}
因特尔cup类
public class IntelCpu {
public void run() {
System.out.println("使用Intel处理器");
}
}
电脑类
public class Computer {
private XiJieHardDisk hardDisk;
private IntelCpu cpu;
private KingstonMemory memory;
public XiJieHardDisk getHardDisk() {
return hardDisk;
}
public void setHardDisk(XiJieHardDisk hardDisk) {
this.hardDisk = hardDisk;
}
public IntelCpu getCpu() {
return cpu;
}
public void setCpu(IntelCpu cpu) {
this.cpu = cpu;
}
public KingstonMemory getMemory() {
return memory;
}
public void setMemory(KingstonMemory memory) {
this.memory = memory;
}
public void run() {
System.out.println("运行计算机");
String data = hardDisk.get();
System.out.println("从硬盘上获取的数据是:" + data);
cpu.run();
memory.save();
}
}
测试类(测试组装的电脑)
public class ComputerDemo {
public static void main(String[] args) {
//创建组件对象
XiJieHardDisk hardDisk = new XiJieHardDisk();
IntelCpu cpu = new IntelCpu();
KingstonMemory memory = new KingstonMemory();
//创建计算机对象
Computer c = new Computer();
//组装计算机
c.setCpu(cpu);
c.setHardDisk(hardDisk);
c.setMemory(memory);
//运行计算机
c.run();
}
}
测试结果
上面代码可以看到已经组装了一台电脑,但是似乎组装的电脑的cpu只能是Intel的,内存条只能是金士顿的,硬盘只能是希捷的,这对用户肯定是不友好的,用户有了机箱肯定是想按照自己的喜好,选择自己喜欢的配件。
案例2:
根据依赖倒转原则进行改进:
代码我们只需要修改Computer类,让Computer类依赖抽象(各个配件的接口),而不是依赖于各个组件具体的实现类。
类图
cpu接口
public interface Cpu {
//运行cpu
public void run();
}
硬盘接口
public interface HardDisk {
//存储数据
public void save(String data);
//获取数据
public String get();
}
内存条接口
public interface Memory {
public void save();
}
英特尔CPU类(实现CPU接口)
public class IntelCpu implements Cpu {
public void run() {
System.out.println("使用Intel处理器");
}
}
希捷硬盘类(实现硬盘接口)
public class XiJieHardDisk implements HardDisk {
//存储数据的方法
public void save(String data) {
System.out.println("使用希捷硬盘存储数据为:" + data);
}
//获取数据的方法
public String get() {
System.out.println("使用希捷希捷硬盘取数据");
return "数据";
}
}
金士顿内存条类(实现内存条接口)
public class KingstonMemory implements Memory {
public void save() {
System.out.println("使用金士顿内存条");
}
}
电脑类(依赖各个组件的接口)
public class Computer {
private HardDisk hardDisk;
private Cpu cpu;
private Memory memory;
public HardDisk getHardDisk() {
return hardDisk;
}
public void setHardDisk(HardDisk hardDisk) {
this.hardDisk = hardDisk;
}
public Cpu getCpu() {
return cpu;
}
public void setCpu(Cpu cpu) {
this.cpu = cpu;
}
public Memory getMemory() {
return memory;
}
public void setMemory(Memory memory) {
this.memory = memory;
}
//运行计算机
public void run() {
System.out.println("运行计算机");
String data = hardDisk.get();
System.out.println("从硬盘上获取的数据是:" + data);
cpu.run();
memory.save();
}
}
测试类
public class ComputerDemo {
public static void main(String[] args) {
//创建计算机的组件对象
HardDisk hardDisk = new XiJieHardDisk();
Cpu cpu = new IntelCpu();
Memory memory = new KingstonMemory();
//创建计算机对象
Computer c = new Computer();
//组装计算机
c.setCpu(cpu);
c.setHardDisk(hardDisk);
c.setMemory(memory);
//运行计算机
c.run();
}
}
解释说明:
面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。 即使实现细节不断变动,只要抽象不变,客户程序就不需要变化。这大大降低了客户程序与实现细节的耦合度。 当组装别的品牌组件时,只需要实现对应的接口即可。
结束!!!!!!!
人们不愿意相信一个土匪的名字叫牧之



