依赖倒置原则
所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
包含三层含义:
1、高层模块不应该依赖低层模块,两者都应该依赖其抽象;
2、抽象不应该依赖细节;
3、细节应该依赖抽象。
举一个例子,假如现在某个人要用钢笔写字,我们可能会这样来实现首先有个一类people,在类里面实现拿起笔写字的功能
public class people {
public void write(pen pen){
System.out.print("我拿起钢笔:");
pen.run();
}
}
然后需要一个钢笔类
public class pen {
public void run(){
System.out.println("开始写字........");
}
}
接下来就是测试实例了,我们通过生成对象来实现钢笔写字功能
public class testdemo {
public static void main(String arg[]){
people dy=new people();
pen pen=new pen();
dy.write(pen);
}
}
结果如下:
我拿起钢笔:开始写字........
但是当我们要用毛笔开始写字怎么办,机器人写字怎么办,此时我们就需要修改people这个类,这样类之间就有较强的耦合性,对代码的扩张造成很大的不便。此时我们就可以把人和笔都抽象出来,等到我们具体要用的时候再进行赋值实现,这样就能够使people和pen这两个类之间减少耦合性。
people
public interface people {
public void write(pen pen);
}
pen
public interface pen {
public void run();
}
我们可以有中国人和机器人都可以
chinese
public class chinese implements people {
@Override
public void write(pen pen) {
// TODO Auto-generated method stub
System.out.print("中国人拿起了笔:");
pen.run();
}
}
robot
public class robot implements people {
@Override
public void write(pen pen) {
// TODO Auto-generated method stub
System.out.print("机器人拿起了笔:");
pen.run();
}
}
笔我们可以有毛笔和铅笔等等
public class brushpen implements pen {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("毛笔开始写字.....");
}
}
public class pencil implements pen {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("铅笔开始写字.....");
}
}
对于任何想写字的people都可以继承people实现write方法,当然他可以用任何种类的笔,如果里面没有这种笔我们可以重新生成这个类,对于原先的代码就不用任何修改就可以实现
测试实例
public class testdemo {
public static void main(String arg[]){
people rb=new robot();
people dy=new chinese();
pen bp=new brushpen();
pen pc=new pencil();
rb.write(pc);
dy.write(bp);
rb.write(bp);
}
}
结果:
机器人拿起了笔:铅笔开始写字.....
中国人拿起了笔:毛笔开始写字.....
机器人拿起了笔:毛笔开始写字.....
依赖倒转原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立,不互相影响,实现模块间的松耦合,只要遵循以下的几个规则就可以:
- 每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备。
这是依赖倒置的基本要求,接口和抽象类都是属于抽象的,有了抽象才可能依赖倒置。
- 变量的显示类型尽量是接口或者是抽象类。
很多书上说变量的类型一定要是接口或者是抽象类,这个有点绝对化了,比如一个工具类,xxxUtils一般是不需要接口或是抽象类的。还有,如果你要使用类的clone方法,就必须使用实现类,这个是JDK提供一个规范。
- 任何类都不应该从具体类派生。
如果一个项目处于开发状态,确实不应该有从具体类派生出的子类的情况,但这也不是绝对的,因为人都是会犯错误的,有时设计缺陷是在所难免的,因此只要不超过两层的继承都是可以忍受的。特别是做项目维护的同志,基本上可以不考虑这个规则,为什么?维护工作基本上都是做扩展开发,修复行为,通过一个继承关系,覆写一个方法就可以修正一个很大的Bug,何必再要去继承最高的基类呢?
- 尽量不要覆写基类的方法。
如果基类是一个抽象类,而且这个方法已经实现了,子类尽量不要覆写。类间依赖的是抽象,覆写了抽象方法,对依赖的稳定性会产生一定的影响。
- 结合里氏替换原则使用
里氏替换原则,父类出现的地方子类就能出现,结合依赖倒置原则,我们可以得出这样一个通俗的规则: 接口负责定义public属性和方法,并且声明与其他对象的依赖关系,抽象类负责公共构造部分的实现,实现类准确的实现业务逻辑,同时在适当的时候对父类进行细化。