4.依赖倒置原则(Dependence Inversion Principle)(DIP)
高层模块不应该依赖底层模块,二者都依赖其抽象; 抽象不应该依赖细节,细节依赖抽象。面向接口编程。
假设我们现在要组装一台电脑,电脑硬件有cpu和内存,而CPU有Intel和AMD两种类型,内存有DDR3 和DDR4两种,那么我们的传统的设计是这样的:
首先我们写出CPU和内存的实现类:
class AMDCpu{
void run() {
System.out.println("AMD CPU在工作");
}
}
class IntelCpu{
void run() {
System.out.println("Intel CPU在工作");
}
}
class DDR3{
void run() {
System.out.println("DDR3 内存在工作");
}
}
class DDR4{
void run() {
System.out.println("DDR4 内存在工作");
}
}
此时,我们要组装一台Intel+DDR3的电脑,那么我们的电脑应该这样写:
class Computer{
private IntelCpu cpu;
private DDR3 memory;
void run() {
this.cpu.run();
this.memory.run();
}
public IntelCpu getCpu() {
return cpu;
}
public void setCpu(IntelCpu cpu) {
this.cpu = cpu;
}
public DDR3 getMemory() {
return memory;
}
public void setMemory(DDR3 memory) {
this.memory = memory;
}
}
public class TraditionDesign {
public static void main(String[] args) {
Computer computer = new Computer();
IntelCpu cpu = new IntelCpu();
computer.setCpu(cpu);
DDR3 ddr3 = new DDR3();
computer.setMemory(ddr3);
computer.run();
}
}
现在电脑能运行了,大体看起来没啥问题,但是如果我们此时想要将内存换程DDR4的或者cpu 换成AMD的,那么我们就需要更改Computer类的属性:
class Computer{
private AMDCpu cpu;
private DDR4 memory;
void run() {
this.cpu.run();
this.memory.run();
}
public AMDCpu getCpu() {
return cpu;
}
public void setCpu(AMDCpu cpu) {
this.cpu = cpu;
}
public DDR4 getMemory() {
return memory;
}
public void setMemory(DDR4 memory) {
this.memory = memory;
}
}
public class TraditionDesign {
public static void main(String[] args) {
Computer computer = new Computer();
AMDCpu cpu = new AMDCpu();
computer.setCpu(cpu);
DDR4 ddr4 = new DDR4();
computer.setMemory(ddr4);
computer.run();
}
}
可以看到,我们想要个更改电脑的配件,那么类就要重新定义属性类型,相应的getter和setter方法也要更改,那么调用getter和setter方法的客户端也要更改代码,更改的地方越多,风险就越大。
依赖倒置原则的核心是:不要面对实现编程,要面对接口或面对抽象类编程。
那么我们看看如果使用依赖倒置原则我们应该如何实现:
首先定义两个接口:
interface Cpu{
void run();
}
interface Memory{
void run();
}
然后实现类实现接口
class AMDCpu implements Cpu{
@Override
public void run() {
System.out.println("AMD CPU在工作");
}
}
class IntelCpu implements Cpu{
@Override
public void run() {
System.out.println("Intel CPU在工作");
}
}
class DDR3 implements Memory{
@Override
public void run() {
System.out.println("DDR3 内存在工作");
}
}
class DDR4 implements Memory{
@Override
public void run() {
System.out.println("DDR4 内存在工作");
}
}
更改我们的Computer属性为接口或抽象类类型的:
/**
* 依赖倒置原则
* @author ZhaoShuai
* @date Create in 2020/4/9
**/
public class DependenceInversionPrinciple {
static class Computer{
private Cpu cpu;
private Memory memory;
void run() {
this.cpu.run();
this.memory.run();
}
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 static void main(String[] args) {
Computer computer = new DependenceInversionPrinciple.Computer();
computer.setCpu(new IntelCpu());
computer.setMemory(new DDR3());
computer.run();
}
}
从上面代码我们可以看出当我们需要组装一台电脑时,我们只需要在客户端设置不同的实现类对象,就可以随意组装电脑而不用更改Computer类。例如我们现在更改为AMD+DDR4:
public static void main(String[] args) {
Computer computer = new DependenceInversionPrinciple.Computer();
computer.setCpu(new AMDCpu());
computer.setMemory(new DDR4());
computer.run();
}
我们之更改了客户端的调用,就完成了一台新电脑的组装,对比这两种是实现方式,我们可以发现
依赖倒置原则的好处有:
`1.代码的可扩展性变好了,我们以后可能在新增新的CPU类型,无需改变现有的类,只需要实现Cpu接口,然后再客户端传递新的类型就行了。`
`2.代码的耦合程度变低了,无论我们想要组装什么类型的电脑,都无需更改Computer类,因为类里的Cpu和Memory是接口类型,我们只要传递的是这个接口的实现,无论传递什么值都可以`
所以依赖倒置原则的核心就是:`应该面向接口或抽象类编程`