依赖倒置原则(Dependence Inversion Principle, DIP)
依赖倒置原则即要依赖于抽象,不依赖于实现。
- 高层模块不应该依赖低层模块,两者都应该依赖其抽象
- 抽象不应该依赖细节
- 细节应该依赖抽象
代码实现
依赖倒置在java中的实现:比如一位妈妈要给孩子讲故事
public class Book {
public String getContext(){
return "从前有座山。。。。。";
}
}
public class Mother {
public void tell(Book book){
System.out.print("mom开始讲故事:");
System.out.println(book.getContext());
}
}
public class Child {
public static void main(String[] args) {
Mother mother = new Mother();
mother.tell(new Book());
}
}
如果我们把书换成日记,报纸之类的,发现妈妈居然读不了日记或报纸,想让妈妈读报纸必须得改变妈妈的代码,这显然是不合理的
public class Diary {
public String getContext(){
return "日记:今天是xxxxx";
}
}
新增一个接口,读物IReader,书籍、日记、报纸等可以读的东西都实现读物接口
public interface IReader {
/**
* 获取所读的内容
* @return
*/
public String getContext();
}
改动后的Mother类
public class Mother {
public void tell(IReader reader){//只要拿到读物的实现类,不管是新闻还是什么,都可以读
System.out.print("mom开始讲");
System.out.println(reader.getContext());
}
}
将之前的Book类和Diary类都实现读物接口
public class Book implements IReader{
@Override
public String getContext() {
return "故事书:从前有座山,。。。。。";
}
}
public class Diary implements IReader{
@Override
public String getContext() {
return "日记:今天是xxxx";
}
}
再想让妈妈读报纸,只需实现读物IReader接口即可
public class NewsPaper implements IReader{
@Override
public String getContext() {
return "报纸:北约停止东扩,乌苏关系降温";
}
}
这样修改之后,无论Child做什么改动,都不需要修改Mother类中的内容,
测试:
实际开发中,Mother类代表高层类,负责完成主要的业务逻辑,如果修改错误的风险极大。开始写的程序Mother依赖于Book类,耦合性较高,是不可取的,Mother必须在Book类加载后才能阅读。
修改后的程序,Mother和Book类、Diary类都是没有联系的。这就是依赖倒置原则的使用,可以降低类之间的耦合性,提高系统的稳定性,降低修改程序造成的风险。
实际编程中一般需做到如下3点:
- 低层模块尽量都要有抽象类或接口,或者两者都有
- 变量的声明类型尽量是抽象类和接口
- 使用继承时遵循里氏代换原则
总结:
IReader相当于基类,IReader出现的地方都可以用实现子类去替换它,这就是里氏代换原则(Liskov Substitution Principle LSP),任何基类可以出现的地方,子类一定可以出现。
里氏代换原则是依赖倒置原则的基础,依赖倒置原则的核心就是要面向接口编程,理解了面向接口编程,也就理解了依赖倒置。