在总结之前先看这样一个问题。
一位客户提出了这样的需求,做一个对正整数加法器。当我们在写这个算法的时候,我们需要传入两个参数,而这两个参数的类型为正数。当我们把这个加法器交给我们的客户的时候,突然客户说不行我们的需求改了,我们还想要加入一个对负整数的加法器(参数为两个负整数)。面对这样的情况我们要怎么办?
于是我们决定“扩大”我们参数的类型,如何“扩大”的呢?我们将参数的类型改为整数,这样不管你的是正数还是负数就都可以接受了。
可是这跟依赖倒置有什么关系。
依赖倒置的原则是说抽象不应该依赖于细节,细节应该依赖于抽象。换言之,要针对接口编程。这样想象一下,我们定义了一个抽象的类为整数。同时定义正整数和负整数继承这个抽象类。
对于依赖倒置原则我们可以给出一个这样的理解方式。抽象类就是一个能够表示更大范围类型的一个类,这个类包括了所有继承它的子类。所以能用整数类型做参数的方法,我一定可以传入一个正整数进去。但是用负整数类型做参数的方法,我是不能够传入一个正整数进去的。面向接口或面向抽象类编程,就是告诉我们尽量要用一个“范围”更大的类型去表示我想要表示的类型。
看这样一个例子(这是从观察者模式中找到的一段代码)
首先定义这样一个员工类,这类员工在老板没在的时候会去看股票。
public class StockObserver { String name; public StockObserver(String name) { this.name=name; } }然后经常看股票的员工就希望老板的秘书能够在老板回来的时候通知他。于是我们定义了一个秘书类,用来通知看股票的同时。
public class Secretary { public void notify(StockObserver observer) { System.out.println(observer.name+",老板回来了"); } }
public static void main(String[] args) { Secretary secretary=new Secretary();//实例化一个秘书 StockObserver sto=new StockObserver("sto");//实例化一个看股票的同事 secretary.notify(sto);//秘书通知看股票的同事 }
现在问题来了经常看NBA的同事也想让这个秘书去通知他。当秘书去通知的时候发现她不知道哪个同事是看NBA的。
从上面的截图来看秘书是不能通知看NBA的员工的。面对这样的情况我们要怎么办?
重写代码
于是我们重新改写通知方法中的参数类型。让它能代表“更广的范围”。
首先我们先写出这个能代表”更广范围“的类(观察者的抽象类)
public abstract class Observer { String name; Observer(String name){ this.name=name; } }
//看股票的员工 public class StockObserver extends Observer{ public StockObserver(String name) { super(name); } }
//看NBA的员工 public class NBAObserver extends Observer{ public NBAObserver(String name) { super(name); } }
关键是是看一下秘书类参数的改变
public class Secretary { //在这里参数类型由看股票的员工改为了所有员工, //也就是只要是继承了抽象类的员工就可以得到秘书的通知 public void notify(Observer observer) { System.out.println(observer.name+",老板回来了"); } }
最后是客户端调用
public static void main(String[] args) { Secretary secretary=new Secretary();//实例化一个秘书 NBAObserver nba=new NBAObserver("nba");//实例化一个看NBA的同事 StockObserver sto=new StockObserver("sto");//实例化一个看股票的同事 secretary.notify(sto);//秘书通知看股票的同事 secretary.notify(nba);//秘书通知看股票 }执行结果
总结
采用依赖倒置原则可以减少类间的耦合,提高系统的稳定性,提高代码的可读性和可维护性。
面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性较低而且大大提高了开发的成本。面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户依赖于抽象,实现的细节也依赖与抽象。即使实现细节不断变动,只要抽象不变客户就不需要变化,这大大降低了客户程序与实现细节的耦合度。