Dependency Inversion Principle , DIP
******************************************************************************************
表述:
1. Abstractions should not depend upon details, details should depend upon abstractions.
2. Program to an interface, not an implementation.
3. 面向接口编程。
4. 抽象不应依赖具体,具体应依赖抽象。
5. 针对接口编程,而不是针对实现编程。
传统的依赖VS.倒置的依赖。
面向接口编程的意思是说,应当使用接口和抽象类进行变量的类型声明,参数的类型声明,方法的返还类型声明,以及数据类型的转换等。
不要针对实现编程的意思就是说,不应当使用具体类进行变量的类型声明,参数类型声明,方法的返还类型声明,以及数据类型的转换等。 要保证做到这一点,一个具体的类应只实现接口和抽象类中声明过的方法,而不应发布(public)另外的方法。 只要一个被引用的对象存在抽象类型,就应当在任何引用此对象的地方使用抽象类型,包括参数的类型声明,方法返还类型的声明,属性变量的类型声明等。
一个抽象类的实现只能由这个抽象类的子类给出,也就是说,这个实现处在抽象类所定义出的继承的等级结构中。而有些语言(如Java)都限制一个类只能从最多一个超类继承,因此将抽象类作为类型定义工具的效能大打折扣。反过来,看接口,就会发现:任何一个实现了一个接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个接口。 然而,如果一个类需要实现两个接口,而这两个接口中存在相同signature的函数,则又当如何? :)
接口VS.抽象类:
接口与抽象类的区别就在于抽象类可以提供某些方法的部分实现,而接口则不可以。这也大概是抽象类唯一的优点。如果向一个抽象类加入一个新的具体方法,那么所有的子类一下子就都得到了这个新的具体方法,而接口做不到这一点。如果向一个接口加入了一个新的方法,则所有实现这个接口的类就全部不能通过编译了,因为它们都没有实现这个新声明的方法。这显然是接口的一个缺点。
从代码重构的角度上讲,将一个单独的具体类重构成一个接口是很容易的。只需要声明一个接口,并将相应的方法添加到接口声明中,然后在具体类定义语句中加上保留字以继承于该接口就行了。 而作为一个已有的具体类添加一个抽象类作为抽象类型则没有那么容易,因为这个具体类有可能已经有一个超类。这样一来,这个新定义的抽象类只好继续向上移动,变成这个超类的超类。如此循环,最后这个新的抽象类必定处于整个类型等级结构的最上端,从而使等级结构中的所有成员都会受到影响。这样就偏离了原来设计的意图。
接口是定义混合类型的理想工具。所为混合类型,就是在一个类的主类型之外的次要类型。一个混合类型表明一个类不仅仅具有某个主类型的行为,而且具有其他的次要行为。
联合使用接口和抽象类:
由于抽象类具有提供缺省实现的优点,而接口具有其他所有优点,所以联合使用两者就是一个很好的选择。 首先,声明类型的工作仍然由接口承担,但是同时给出的还有一个抽象类,以给出一个缺省实现。其他同属于这个抽象类型的具体类可以选择实现这个接口,也可以选择继承自这个抽象类。如果一个具体类直接实现这个接口的话,它就必须自行实现所有的接口;相反,如果它继承自抽象类的话,它可以省去实现一些方法,因为它可以从抽象类中自动得到这些方法的缺省实现;如果需要向接口加入一个新的方法,那么只要向这个抽象类加入这个方法的一个具体实现就可以了,因为所有继承自这个抽象类的子类都会从这个抽象类得到这个具体方法。这其实就是缺省适配器模式(Default Adapter).
依赖倒置原则DIP告诉我们:应该优先依赖于抽象类,而避免依赖于具体类。其中的原因就在于:这些具体类有可能会改变。但是,如果这个具体类是稳定的,那么依赖它就不会出现麻烦。