依赖倒置原则:其定义是说,高层模块不应该依赖于低层模块,二者之间都应该依赖于其各自的抽象;抽象不应该依
赖于细节,而细节应该依赖于抽象
上述的说法,可能很绕口。简单点理解,打个比方,我们电脑如果要读取U盘中的文件内容,那么我们只需要在电脑
的USB接口中插入我们的U盘即可,而不是采用电烙铁将我们U盘烙在电脑的某个线路上。在这个例子中,电脑算是
一个模块,U盘也算是一个模块,电脑读取U盘,那么电脑属于高层模块,U盘属于低层模块,他们二者要进行数据交
互,依赖的是各自的抽象(接口),而不是依赖的各自底层实现的线路;U盘不管是金士顿的,还是其他牌子,电脑
都不管,都可以读取内容,也就是抽象不依赖于细节,但是无论是金士顿的U盘,还是其他牌子,都要通过USB接口
才能与电脑进行数据交互,也就是细节依赖于抽象。
相对于细节的多变性,抽象于则要稳定的多。在JAVA中,抽象指的是接口或抽象类,而细节则是具体的实现类,模
块与模块之间进行交互,最好采用接口或者抽象类制定好规范和契约,而不要去涉及具体的操作,把具体的细节交由
它们各自的实现类即可。
依赖倒置原则的核心思想正是像电脑与U盘的实现一样,采用面向接口编程。由接口定义一系列各自的规范和契约,
具体的实现则由接口的实现类去完成,那么如果有天需要更换具体的实现类时,只需要更换具体的实现即可,而不用
去关心到底是哪一个实现类需要更换,或者说如何更换。
看了网上一个例子,感觉很不错,也就是母亲给孩子讲故事的那个示例
当客户端,需要母亲讲故事时,调用母亲的read(),并传递一本具体的书进去,好了,母亲正常给小孩讲讲故事……
可以突然某一天,小孩要求母亲给他读报,怎么办呢?难道我们将read()里面的参数修改为Newspaper么,那后面又
需要读书,或者读故事会怎么办?所以上述的设计,显然不是一个很好的设计……
首先,我们分析,上述这个示例中,我们可以抽象出2个对象类型,1、读者,2、读物,那么我们就围绕着这2个类型
进行设计
上面这种设计,就不用关心到底是谁来讲,讲什么了,如果需要扩展,那么直接实现接口即可。
代码演示:
package yuanze;
/**
* 抽象读者
*
* @author Administrator
*
*/
public interface Reader {
public String read(Resource resource);
}
具体的读者,可以是母亲,可以是父亲……
package yuanze;
public class Mother implements Reader {
@Override
public String read(Resource resource) {
// TODO Auto-generated method stub
return resource.getContent();
}
}
抽象读物:
package yuanze;
/**
* 把任何读物都认为是一种资源
*
* @author Administrator
*
*/
public interface Resource {
public String getContent();
}
具体的读物,可以是报纸,可以是书籍:
package yuanze;
public class NewsPaper implements Resource {
@Override
public String getContent() {
// TODO Auto-generated method stub
return "今天的新闻是:……";
}
}
package yuanze;
public class Book implements Resource {
private String name;
private String content;
public Book() {
super();
// TODO Auto-generated constructor stub
}
public Book(String name) {
super();
this.name = name;
}
public Book(String name, String content) {
super();
this.name = name;
this.content = content;
}
@Override
public String getContent() {
// TODO Auto-generated method stub
return "书名是: 《" + name + "》,内容是:" + content;
}
}
好,客户端调用开始:
package yuanze;
public class Test {
public static void main(String[] args) {
// 母亲读取书籍
Reader reader = new Mother();
Resource resource = null;
resource = new Book("钢铁是如何炼成的", "这一本关于励志的书籍,书的内容是……");
String content = reader.read(resource);
System.out.println(content);
// 母亲现在开始读报纸
resource = new NewsPaper();
content = reader.read(resource);
System.out.println(content);
}
}
好,如果需要扩展父亲来读取的话,只需要将母亲的实例修改为父亲的实例即可了。这就是依赖倒置原则带来的好
处,扩展性得以提升。
关于这个依赖倒置的好处,我们后面在使用SpringIOC容器注入实例后,就更清晰了。那么在实际编程中,我们需要
做到以下几点:
1、低层模块尽量都要有抽象类或接口,或者两者都有。
2、变量的声明类型尽量是抽象类或接口。
3、在使用继承时,遵循里氏替换原则。