六、依赖倒置原则(Dependency Inversion Principle)
依赖抽象,不依赖具体类。
扯淡:其实我有时想,也许类继承上这种树状结构,理想状态会是仅仅有叶子节点是具体类(所有具体类都是叶子节点,叶子节点可能不是具体类)。
这个原则说明了:不能让高层组件依赖低层组件,而且,不管高层或低层组件,“两者”都应该依赖抽象。
所谓高层组件,是由其他低层组件定义其行为的类。例如上例中的借阅管理系统,它的行为是由可借阅的对象决定的,而可借阅的接口则是一个低层组件。
其实,这个原则听起来很像是“针对接口编程,不针对实现编程”,不是吗?的确很相似,然而这里更强调“抽象”。
另外,我觉得“针对接口编程”是从实现者(被调用者)的角度来看“接口”,而“依赖倒置原则”是从调用者的角度来看:
OO是符合人的思维的,人在某一个时刻理解某一个对象,往往只是看他们的某个侧面,而并不是他们的全部。
假设,我在公司里组织了一批Java程序员,我和这些人在公司里交流是通过“Java程序员”这个统一的接口,而“Java程序员”可能并非他们的全部。
模拟到代码里,就是这么人的类"class Person extends ... implements Java程序员 ..."。
而在我和他们交互时,仅仅使用了"Java程序员 一号员工 = [张三];",这是比较符合生活逻辑的。他们各自在家庭里可能有充当了“儿子”、“女儿”等不同的身份。而我们在公司这个环境下,只关心他们是“Java程序员”这一部分的特性,这才是合理的。
因此,直接用"Java程序员 一号员工 = new Person("张三");",这样针对实现来写,就太过具体,让我们关心的事情太多了,有很多冗余。不符合此原则。
于是引入“工厂模式”等“创建型模式”就是为了避免违背这一原则。
下面的指导方针,能帮你避免在OO设计中违反依赖倒置原则:
变量不一可以持有具体类的引用。
如果使用new,就会持有具体类的引用。你可以改用工厂来避开这样的做法。不要让类派生自具体类。
如果派生自具体类,你就会依赖具体类。请派生自一个抽象(接口或抽象类)。不要覆盖基类中已实现的方法。
如果覆盖基类已实现的方法,那么你的基类就不是一个真正适合被继承的抽象。基类中已实现的方法,应该由所有的子类共享。
正如同我们的许多原则一样,应该尽量达到这个原则,而不是随时都要遵循这个原则。我们都很清楚,任何Java程序都有违反这些指导方针的地方!
但是,如果你深入体会这些方针,将这些方针内化成你思考的一部分,那么在设计时,你将知道何时有足够的理由违反这样的原则。比方说,如果有一个不像是会改变的类,那么在代码中直接实例化具体类也就没什么大碍。我们平时不是在程序中不假思索地就实例化字符串对象吗?就没有违反这个原则?当然有!可以这么做吗?可以!为什么?因为字符串不可能改变。
另一方面,如果有个类可能改变,你可以采用一些好技巧(例如工厂方法)来封装改变。
评论
发表评论