里氏代换原则
面向对象设计的重要原则是创建抽象化,并且从抽象化导出具体化,具体化也就是给出不同的实现。
继承关系就是一种从抽象化到具体化的导出。
里氏代换原则:如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型。
其实就是一个软件程序的代码块如果使用的是一个基类的话,那么一定适用于其子类,而且它根本不能察觉出基类对象和子类对象的区别。
依赖倒转原则
面向对象设计的原则估计都是和抽象分不开的,依赖倒转原则:要依赖于抽象,不要依赖于具体。
传统的过程性系统的设计办法倾向于使高层次的模块依赖于低层次的模块,抽象层次依赖于具体层次。倒转原则就是把这个错误的依赖关系倒转过来。
抽象层次包含的应该是应用系统的商务逻辑和宏观的、对整个系统来说重要的战略性决定,是必然性的体现。
具体层次含有的是一些次要的与实现有关的
这一设计原则也是COM、CORBA、JavaBean、EJB等构建设计模型背后的基本原则。
从复用的角度来说,高层次的模块是应当复用的,而且是复用的重点,因为它含有一个应用系统最重要的宏观商务逻辑,是较为稳定的。在传统的过程性设计中,复用则侧重于具体层次模块的复用。
同样,最重要的宏观商务逻辑也是维护的重点。
遵守依赖倒转原则会带来复用和可维护性的“倒转”。
依赖(或者耦合)关系的种类:
零耦合(Nil Coupling)关系:两个类没有耦合关系
具体耦合(Concrete Coupling)关系:发生在两个具体的(可实例化的)类之间,经由一个类对另一个具体类的直接引用造成
抽象耦合(Abstract Coupling)关系:发生在一个具体类和一个抽象类(或Java接口)之间,使两个必须发生关系的类之间存有最大的灵活性
依赖倒转原则的表述是:
抽象不应当依赖于细节,细节应当依赖于抽象。
Abstractions should not depend upon details. Details should depend upon abstractions.
要针对接口编程,不要针对实现编程。
Program to an interface, not an implementation.
也就是说应当使用Java接口和抽象Java类进行变量的类型声明、参数是类型声明、方法的返还类型说明,以及数据类型的转换等。而不要用具体Java类进行变量的类型声明、参数是类型声明、方法的返还类型说明,以及数据类型的转换等。
要保证做到这一点,一个具体Java类应当只实现Java接口和抽象Java类中声明过的方法,而不要给出多余的方法。
要遵守“开—闭”原则,倒转依赖原则便是达到要求的途经。
变量的静态类型和真实类型
变量被声明时的类型是静态类型(static type),或明显类型(apparent type),变量所引用的对象的真实类型叫实际类型(actual type)。
对象的创建
变量的声明是静态类型,可实例创建过程中调用的构造方法仍然必须是具体类型的构造方法。一般,在创建一个对象时,Java语言要求使用new关键词以及这个类本身,一旦对象被创建,就可以灵活地使用这个对象的抽象类型来引用它。所以,创建一个对象的过程是违背“开—闭”原则以及依赖倒转原则的,从而就产生了工厂模式,用于解决对象创建过程中的依赖倒转问题。
怎样做到依赖倒转?
以抽象方式耦合是依赖倒转原则的关键。抽象耦合关系总要涉及具体类从抽象类继承,并且需要保证在任何引用到基类的地方都可以改换成其子类,因此,里氏代换原则是依赖倒转原则的基础。
在抽象层次上的耦合虽然有灵活性,但也带来了额外的复杂性,如果一个具体类发生变化的可能性非常小,那么抽象耦合能发挥的好处便十分有限,这时可以用具体耦合反而会更好。
依赖倒转原则的优缺点
虽然很强大,但却最不容易实现。因为依赖倒转的缘故,对象的创建很可能要使用对象工厂,以避免对具体类的直接引用,此原则的使用可能还会导致产生大量的类,对不熟悉面向对象技术的工程师来说,维护这样的系统需要较好地理解面向对象设计。
依赖倒转原则假定所有的具体类都是会变化的,这也不总是正确,有一些具体类可能是相当稳定,不会变化的,使用这个具体类实例的应用完全可以依赖于这个具体类型,而不必为此发明一个抽象类型。