Dependence Inversion Principle ”依赖倒置原则
说明: 要依赖于抽象,不要依赖于具体。客户端依赖于抽象耦合。
抽象不应当依赖于细节;细节应当依赖于抽象;
要针对接口编程,不针对实现编程。
优点: 使用传统过程化程序设计所创建的依赖关系,策略依赖于细节,这是糟糕的,因为策略受到细节改变的影响。依赖倒置原则使细节和策略都依赖于抽象,抽象的稳定性决定了系统的稳定性。
怎样做到依赖倒置?
以抽象方式耦合是依赖倒转原则的关键。抽象耦合关系总要涉及具体类从抽象类继承,并且需要保证在任何引用到基类的地方都可以改换成其子类,因此,里氏代换原则是依赖倒转原则的基础。
在抽象层次上的耦合虽然有灵活性,但也带来了额外的复杂性,如果一个具体类发生变化的可能性非常小,那么抽象耦合能发挥的好处便十分有限,这时可以用具体耦合反而会更好。
层次化:所有结构良好的面向对象构架都具有清晰的层次定义,每个层次通过一个定义良好的、受控的接口向外提供一组内聚的服务。
依赖于抽象:建议不依赖于具体类,即程序中所有的依赖关系都应该终止于抽象类或者接口。尽量做到:
1 、任何变量都不应该持有一个指向具体类的指针或者引用。
2 、任何类都不应该从具体类派生。
3 、任何方法都不应该覆写它的任何基类中的已经实现的方法。
上面所叙述的只是一些理论性的东西,下面举个例子或许能更好地说明问题,
首先假设有一个需求,类Business需要调用类Dependency的方法f(),按照日常的做法,得到下面的代码:
//**类Dependency**
public class Dependency {
public void f() {};
}
//**类Business**
public class Business {
Dependency d;
public Business() {
d = new Dependency();
}
public void doSth() {
d.f();
}
}
对上述实现做出如下修改:
首先,将Business里的Dependency实例的获得该为setter方式,其次,将Dependency类改为某个接口的实现。故可以得到下面新的代码:
//**接口IDependency**
public interface IDependency {
void f();
}
//**类Dependency**
public class Dependency {
public void f() {};
}
//**类Business**
public class Business {
IDependency d; //如果在构造函数里new一个具体的类的话,那么这段代码还是不能复用,一些书上在这个类中的某个函数中new一个具体的类,当然
public Business() {} //在具体类发生变化的时候,这段代码还得改,还是不能复用,高层还是依赖于具体,所以对读者是一种误解。所以应该采取依赖注
public void doSth() { //的方法,让外界来决定什么时候来传入一个具体的类,这就是好莱芜原则,只要具体类实现了某个稳定的接口,那么这个类
d.f(); //Business就能在以后的开发中得到复用,所以不只是继承才能复用,继承只是比较狭隘的复用,面向对象的复用强调的是逻辑的
} //复用,只有这样才能在开发中节省大量的人力。所以依赖倒置应该于依赖注入联系起来,不能分离使用(个人看法)。
public void setDependency(IDependency d) {
this.d = d;
}
}
在新的代码中,首先Business的变量d可以接收任何IDependency的实例,另外,Dependency的实例不是通过Business来获得,而是通过setter(也可以用构造器)来由外部传给它。这似乎跟我们往常的代码没什么不同,但这已经是一个良好的设计。