总结:
面向对象的六大原则:
1.单一职责原则(Single Responsibility Principle)
2.开闭原则 (Open Close Principle)
3.里氏替换原则(Liskov Substitution Principle)
4.依赖倒置原则(Dependence Inversion Principle)
5.接口隔离原则(InterfaceSegregation Principle)
6.迪米特原则(Law of Demeter)
第一:单一职责原则
很多人的老把这个原则解释为一个类只能干一件事,但是我更愿意接受的解释是:一个类只能因为一种原因而被改变。作者认为,一个类应该是一组相关性很高的函数和接口的封装。作者谈到,在初入职场时,领导交给他一个封装图片加载框架(ImageLoader)的任务,于是作者毫不犹豫的就将加载图片和缓存图片两个功能都同时放在了一个类中。很显然这是违背了单一职责原则的,因为如果当日后缓存方式有需要改变的时候代码逻辑就需要重新修改了,那样可能会产生新的Bug。于是,作者将ImageLoader拆分为ImageLoader和ImageCache两个类。这显然要比第一次做的好。
第二:开闭原则
开闭原则的定义是:软件中的对象(类.模块.函数等)应该对于扩展是开放的,而对于修改是封闭的。
为什么对于修改应该是封闭的呢?因为,在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会将错误引入到原本已经经过测试的旧代码中,破坏原有系统。
因此,在当软件需要变化时,我们应该尽量通过扩展的方式来实现变化,而不是通过修改已有的代码来实现。因此,对于扩展应该是开放的。
以作者第一小节的例子来看,一旦需要修改图片的缓存策略时,该框架无法基于现有的类进行扩展。因此该框架不满足软件设计的开闭原则,需要对其进行修改,而不是简简单单的扩展。作者的领导将ImageCache抽象出来,成为一个抽象类,而ImageLoader除了支持默认的内存缓存外,还支持自定义的缓存类(通过setImageCache方法来修改缓存具体实现类),这样当客户端需要修改缓存策略时,只需要继承自ImageCache,并将其实例传给ImageLoader就可以实现了。这样对于ImageLoader的修改就可以达到封闭的目的,而对于缓存策略的具体实现又是开放的、可拓展的。
第三:里氏替换原则
由于第一种定义太过拗口,简直可以说讲的不是人话,所以我们更容易接受第二种定义(其实意思是一样的):所有引用基类的地方必须能够透明地使用其子类的对象。通俗一点讲,只要父类能够出现的地方子类就可以出现,而且替换为子类也不会产生任何的异常或者错误。
里氏替换原则的核心原理是抽象,抽象又依赖于继承这个特性。在oop当中,继承的优缺点都是相当明显的。
优点:
1.代码重用,减少类创建的成本,每个子类都用于父类的方法和属性。
2.子类和父类基本相似,但又与父类有所区别
3.提高代码的可扩展性
缺点:
1.继承是侵入性的,只要继承就必须拥有父类的所有属性和方法
2.可能造成子类代码冗余、灵活性降低,因为子类必须拥有父类的属性和方法
第四:依赖倒置原则
依赖倒置原则有以下几个关键点:
1.高层模块不应该依赖底层模块,两者都应该依赖其抽象
2.抽象不应该依赖细节
3.细节应该依赖抽象
依赖倒置原则在Java语言中的表现就是:模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。
第五:接口隔离原则
接口隔离原则的目的是将非常庞大、臃肿的接口拆分称更小的和更具体的接口,以系统解开耦合从而容易重构、更改和重新部署。
Bob大叔在21世纪早期将单一职责、开闭原则、里氏替换、接口隔离和依赖倒置5个原则定义为SOLID原则。
第六:迪米特原则
一个对象应该对其他对象有最少的了解。
作者用租户通过中介租房子的例子来解释了迪米特原则,租户的目的很简单,我只给你房间的面积和价格,中介给我找到合适的房子就行,其他的我一概不管。反面例子中,租户类不仅与中介类有耦合,还与Room耦合,这违背了对其他对象有最少的了解。该例子中,租户不需要与Room发生耦合,租户只关心结果,剩下的事应该都让中介去做(比较价钱和面积是否合适)。所以,正确的写法应该是租户依赖中介而中介依赖Room。通俗一点来说,租户不关心你中介的实现细节,我只关心结果。
小结:
在应用开发过程中,最难的不是完成应用的开发工作,而是在后续的升级中、维护过程中让应用能够拥抱变换。拥抱变化也就意味着在满足需求且不破坏系统稳定性的前提下保持高可扩展性、高内聚、低耦合,在经历了各版本的变更之后依然保持清晰、灵活、稳定的系统架构。当然,这是一个理想的情况,但我们必须要朝着这个方向去努力,那么遵循面向对象六大原则就是我们走向灵活软件之路所迈出的第一步。
网友评论