一、依赖倒置原则
1.定义
高层模块不应该依赖低层模块,两者都应该依赖其抽象;
抽象不应该依赖细节,细节应该依赖抽象。
简单的说:面向接口编程,而不是面向实现编程。通过依赖于抽象,系统可以更加灵活、易于扩展和维护。
2.代码举例
- 接口定义 :
Cpu
、Disk
和Memory
是三个接口,分别定义了run
、save
、get
、save
方法。
- 实现类 :
IntelCpu
实现了Cpu
接口。ChangChengDisk
实现了Disk
接口。KingstonMemory
实现了Memory
接口。
- 计算机类
Computer
:Computer
类通过set
方法注入 CPU、硬盘和内存,使用接口作为参数类型。
- 客户端代码
ComputerDemo
:ComputerDemo
创建了具体的组件实例,并将它们注入到Computer
中进行使用。
java
public interface Cpu {
public void run();
}
public interface Disk {
//存储数据
public void save(String data);
//读取数据
public String get();
}
public interface Memory {
public void save();
}
public class IntelCpu implements Cpu{
@Override
public void run() {
System.out.println("Intel的Cpu运行了");
}
}
public class ChangChengDisk implements Disk{
@Override
public void save(String data) {
System.out.println("使用长城硬盘存储数据");
}
@Override
public String get() {
System.out.println("使用长城硬盘读取数据");
return "数据";
}
}
public class KingstonMemory implements Memory{
@Override
public void save() {
System.out.println("使用kingston的内存条");
}
}
public class Computer {
private Memory memory;
private Disk disk;
private Cpu cpu;
public Memory getMemory() {
return memory;
}
public void setMemory(Memory memory) {
this.memory = memory;
}
public Disk getDisk() {
return disk;
}
public void setDisk(Disk disk) {
this.disk = disk;
}
public Cpu getCpu() {
return cpu;
}
public void setCpu(Cpu cpu) {
this.cpu = cpu;
}
public void run(){
System.out.println("计算器执行!");
String data = disk.get();
System.out.println("从硬盘上获取的数据是:"+data);
cpu.run();
memory.save();
}
}
public class ComputerDemo {
public static void main(String[] args) {
Disk disk = new ChangChengDisk();
Cpu cpu = new IntelCpu();
Memory memory = new KingstonMemory();
Computer computer = new Computer();
computer.setCpu(cpu);
computer.setDisk(disk);
computer.setMemory(memory);
computer.run();
}
}
3.代码讲解
(1)Computer 类依赖于抽象(接口),而不是具体实现
Computer
类中,Cpu
、Disk
和Memory
都是接口类型。这意味着Computer
类并不知道具体的实现类(如IntelCpu
、ChangChengDisk
、KingstonMemory
),而是依赖于这些接口。- 依赖倒置原则体现 :
- 高层模块(Computer) 依赖于 抽象接口(Cpu、Disk、Memory) ,而不是具体实现。这使得
Computer
类与具体组件之间解耦,可以方便地替换不同的硬件实现。
- 高层模块(Computer) 依赖于 抽象接口(Cpu、Disk、Memory) ,而不是具体实现。这使得
(2)实现类依赖于抽象,而不是相互依赖
IntelCpu
、ChangChengDisk
和KingstonMemory
类都实现了各自对应的接口。- 依赖倒置原则体现 :
- 具体实现类 依赖于接口(如
Cpu
、Disk
、Memory
),而不是直接依赖于Computer
类。这保证了不同的实现类可以独立发展,而不会影响到计算机系统的设计。
- 具体实现类 依赖于接口(如
(3)客户端代码灵活性
- 客户端可以自由地选择不同的硬件实现(比如替换
ChangChengDisk
为SamsungDisk
,替换IntelCpu
为AMDCpu
),而无需修改Computer
类的任何代码。 - 依赖倒置原则体现 :
- 客户端通过接口进行依赖注入,而不是直接依赖具体类。这样就可以很方便地替换不同的实现,而不会影响整体系统的功能。
4. 改进点:依赖注入方式
目前,使用的是 Setter 注入 (setCpu
、setDisk
、setMemory
方法)。在实际开发中,我们还有其他的 依赖注入 方式,如:
- 构造器注入 :
- 在创建
Computer
实例时,通过构造器注入所需的硬件组件,可以确保这些组件不会为空。
- 在创建
- 接口注入 :
- 通过接口定义注入的方法,保证
Computer
类能够灵活注入不同的组件。
- 通过接口定义注入的方法,保证
5.总结
- 高层模块(Computer 类) 依赖于 抽象(接口) ,而不是具体实现。这使得
Computer
类能够在不修改自身代码的情况下,适应不同的硬件组件。 - 低层模块(IntelCpu、ChangChengDisk、KingstonMemory 类) 实现了对应的接口(Cpu、Disk、Memory),符合 "低层模块依赖于抽象" 的原则。
- 灵活性与可扩展性 :这种设计模式使得系统具有很强的扩展性,能够轻松地替换不同的实现,如使用新的
AMD
CPU 或SamsungDisk
,而无需修改核心Computer
类的逻辑。