设计模式七大原则之依赖倒转原则
什么是依赖倒转原则
- 高层模块不应该依赖底层模块,二者都应该依赖其抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。
说这样的官话确实比较难以理解,那么来说点人话:
- 依赖倒转原则主要是要体现出**“面向接口编程”**的中心思想。
- 细节是多变的,抽象是相对稳定的,以抽象为基础搭建的结构会比以细节为基础的架构稳定很多。抽象指接口、抽象类,细节为具体的实现类。
- 在实现功能时,要多用接口或抽象类去制定一个规范,比如传参时更多地使用接口或抽象类来规定变量,而非某一个具体的实现类,这样在扩展功能时,不需要修改现有代码,仅需要使新增的类实现该接口,即可完成功能扩展。
- 低层的模块尽量要有抽象类或接口,当然也可以两者都有,可以提高程序的稳定性。
- 变量的声明类型尽量是抽象类或接口,这样可以使得变量引用和实际对象之间有一个缓冲,便于程序扩展和优化。
所谓倒转:
- 接口和抽象类是抽象的,他们依赖一个实现类去表现出其功能,但这样的依赖是对细节的依赖,在软件上是很不稳定的。
- 倒转即是让具体的依赖抽象的,增强稳定性与可扩展性,同时与具体实现类解耦。
注意点:
- 继承时应当遵循里氏代换原则。
举个栗子
书籍是人类进步的阶梯,现在需要实现一个Person类,其包括一个read方法,用于表示人读书的行为。
最简单的实现方式:
public class DependecyInversion {
public static void main(String[] args) {
Person person = new Person();
person.read(new Book());
}
}
class Book {
public String getContent() {
return "书里面写着:知为行之始,行为知之成。";
}
}
class Person {
public void read(Book book) {
System.out.println(book.getContent());
}
}
这样我们实现的Person的read功能。
但是时代在进步,电子书出现了,人们可以阅读纸质书,用kindle阅读,甚至用手机阅读。现在很明显,上面的代码是不可用的,因为它过度依赖Book类这一细节。
public class DependecyInversion {
public static void main(String[] args) {
Person person = new Person();
person.read(new Book());
person.read(new Ebook());
}
}
interface BookInterface {
String getContent();
}
class Book implements BookInterface {
@Override
public String getContent() {
return "书里面写着:知为行之始,行为知之成。";
}
}
class Ebook implements BookInterface {
@Override
public String getContent() {
return "电子书里写着:知为行之始,行为知之成。";
}
}
class Person {
public void read(BookInterface book) {
System.out.println(book.getContent());
}
}
现在,Person中的read方法仅需要获取一个可以被阅读的“书”即可以进行阅读(仅依赖一个接口),无论是纸质书还是电子书,均可以加以扩展,不必对Person类进行其他修改(如方法重载等)。
依赖传递的三种方法:
- 接口传递
上面的例子中,就是使用了接口传递的方法传递依赖:
class Person {
public void read(BookInterface book) { //方法参数中用接口规定一个变量
System.out.println(book.getContent());
}
}
public class DependecyInversion {
public static void main(String[] args) {
Person person = new Person();
person.read(new Book()); //调用时传入具体的书籍
person.read(new Ebook());
}
}
- 构造方法传递
class Person {
BookInterface book;//将具体书籍作为一个属性存在
public Person(BookInterface book) {//构造方法
this.book = book;
}
public void read(){
System.out.println(book.getContent());
}
}
public class DependecyInversion {
public static void main(String[] args) {
Person person = new Person(new Book());//给定其书籍属性
person.read();//让该对象开始阅读即可
}
}
- setter方式传递
class Person {
BookInterface book;
public void read(){
System.out.println(book.getContent());
}
public void setBook(BookInterface book) {
this.book = book;
}
}
public class DependecyInversion {
public static void main(String[] args) {
Person person = new Person();
person.setBook(new Book());//要在调用read方法前通过set方法给定一个能被阅读的书,否则很明显会出现NullPointerException
person.read();
}
}