定义:高层模块不应该依赖低层模块,两者都应该依赖其抽象(接口或抽象类);抽象不应该依赖细节(具体的实现类),细节应该依赖抽象。
核心思想:要面向接口编程,不要面向实现编程。
依赖倒置原则是实现开闭原则的重要途径之一,它降低了客户与实现模块之间的耦合。
由于在软件设计中,细节具有多变性,而抽象层则相对稳定。因此,以抽象为基础搭建起来的架构,要比以细节为基础搭建起来的架构稳定得多。
使用接口或者抽象类的目的是制定好规范和约束,而不去涉及任何具体的操作,把实现细节的任务交给它们的实现类去完成。
实现方法:依赖倒置原则的目的是:通过面向接口的编程来降低类之间的耦合性。
所以,我们在实际编程中遵循以下4点,就能在项目中满足这个规则。
1.每个类尽量提供接口或抽象类,或者两者都具备。
2.变量的声明类型尽量是接口或者抽象类。(都依赖抽象)
3.任何类都不应该从具体类派生。
4.使用继承尽量遵循里氏替换原则。
应用:下面通过“组装电脑”案例,来体会依赖倒置原则。
分析:现要组装一台电脑,需要配件cpu,硬盘,内存条(还有其它组件,只是拿这三个举例子)。只有这些配置都有了,计算机才能正常运行。cpu有很多选择,如Intel,AMD等,硬盘可以选择捷西,西数等,内存条可以选择金士顿,海盗船等。
1.捷西硬盘类(XiJieHardDisk),包含存储数据和读取数据的方法。
public class XiJieHardDisk{
//存储数据
public void save(String data) {
System.out.println("使用希捷硬盘存储数据为:"+data);
}
//获取数据
public String get() {
System.out.println("使用希捷硬盘存储数据");
return "数据";
}
}
2.Intel处理器类(IntelCpu),包含运行CPU的方法。
public class IntelCpu {
//运行CPU
public void run() {
System.out.println("使用Intel处理器");
}
}
3.金士顿内存条类(KingstonMemory),包含存储数据的方法。
public class KingstonMemory {
//存储数据
public void save() {
System.out.println("使用金士顿内存条");
}
}
4.电脑类(Computer)
public class Computer {
//组装电脑需要组件,所以聚合不同组件类对应的对象
private XiJieHardDisk hardDisk;
private IntelCpu cpu;
private KingstonMemory memory;
/**
* 运行电脑
*/
public void run(){
System.out.println("运行计算机");
//开机会先加载硬盘,获取数据
String data = hardDisk.get();
System.out.println("从硬盘上获取的数据是:"+data);
cpu.run();
memory.save();
}
//get和set方法(set方法用于组装计算机)
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;
}
}
5.测试类(ComputerDemo)
public class ComputerDemo {
public static void main(String[] args) {
//1,创建计算机对象(此时计算机还只是一个空壳)
Computer c = new Computer();
//2,创建计算机组件
XiJieHardDisk hardDisk = new XiJieHardDisk();
IntelCpu cpu = new IntelCpu();
KingstonMemory memory = new KingstonMemory();
//3,组装计算机
c.setCpu(cpu);
c.setHardDisk(hardDisk);
c.setMemory(memory);
//4,运行计算机
c.run();
}
}
6.运行结果
运行结果分析:上面的代码已经实现了组装一台电脑,但是组装的电脑的cpu只能是Intel的,内存条只能是金士顿的,硬盘只能是希捷的。但如果用户想按照自己的喜好选择自己喜欢的配件,就需要修改原来的代码,这就违背了“开闭原则”。
举个栗子,假如用户想要用AMD的处理器(CPU),就需要修改Computer类中的代码:
public class Computer {
//组装电脑需要组件,所以聚合不同组件类对应的对象
private XiJieHardDisk hardDisk;
private AMDCpu cpu;
private KingstonMemory memory;
/**
* 运行电脑
*/
public void run(){
System.out.println("运行计算机");
//开机会先加载硬盘,获取数据
String data = hardDisk.get();
System.out.println("从硬盘上获取的数据是:"+data);
cpu.run();
memory.save();
}
//get和set方法(用于组装计算机)
public XiJieHardDisk getHardDisk() {
return hardDisk;
}
public void setHardDisk(XiJieHardDisk hardDisk) {
this.hardDisk = hardDisk;
}
public AMDCpu getCpu() {
return cpu;
}
public void setCpu(AMDCpu cpu) {
this.cpu = cpu;
}
public KingstonMemory getMemory() {
return memory;
}
public void setMemory(KingstonMemory memory) {
this.memory = memory;
}
}
存在以上缺点的原因:计算机类设计时同具体的组件类(XiJieHardDisk、IntelCpu、KingstonMemory)绑定了,这违背了“依赖倒置原则”。
改进方法:把希捷硬盘向上抽取出一个父接口HardDisk,Intel处理器向上抽取出一个父接口CPU,金士顿内存条向上抽取出一个父接口Memory。Computer类依赖接口,面向接口编程。这样一来,不管用户想用什么组件组装计算机(Computer),都不需要修改原有的代码,符合“开闭原则”。
改进后代码:
1.硬盘接口(HardDisk)
public interface HardDisk {
/**
* 存储数据
* @param data
*/
public void save(String data);
/**
* 获取数据
* @return
*/
public String get();
}
1.1希捷硬盘(XiJieHardDisk)
public class XiJieHardDisk implements HardDisk {
@Override
public void save(String data) {
System.out.println("使用希捷硬盘存储数据为:"+data);
}
@Override
public String get() {
System.out.println("使用希捷硬盘存储数据");
return "数据";
}
}
2.CPU接口(CPU)
public interface Cpu {
/**
* 运行cpu
*/
public void run();
}
2.1Intel处理器(IntelCpu)
public class IntelCpu implements Cpu {
@Override
public void run() {
System.out.println("使用Intel处理器");
}
}
3.内存条接口(Memory)
public interface Memory {
/**
* 内存条存储数据
*/
public void save();
}
3.1金士顿内存条(KingstonMemory)
public class KingstonMemory implements Memory {
@Override
public void save() {
System.out.println("使用金士顿内存条");
}
}
4.计算机类(Computer),依赖接口
public class Computer {
/**
* 依赖于抽象
*/
private HardDisk hardDisk;
private Cpu cpu;
private Memory memory;
/**
* 运行电脑
*/
public void run(){
System.out.println("运行计算机");
String data = hardDisk.get();
System.out.println("从硬盘上获取的数据是:"+data);
cpu.run();
memory.save();
}
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;
}
}
5.测试类
public class ComputerDemo {
public static void main(String[] args) {
//1,创建计算机对象
Computer c = new Computer();
//2,创建计算机组件
HardDisk hardDisk = new XiJieHardDisk();
Cpu cpu = new IntelCpu();
Memory memory = new KingstonMemory();
//3,组装计算机
c.setCpu(cpu);
c.setHardDisk(hardDisk);
c.setMemory(memory);
//4,运行计算机
c.run();
}
}
6.运行结果
{{ cmt.username }}
{{ cmt.content }}
{{ cmt.commentDate | formatDate('YYYY.MM.DD hh:mm') }}