面向对象设计的五大原则
2015-10-28 13:29
232 查看
Bob大叔在《敏捷软件开发》一书中给出了面向对象设计的五大原则:SRP,OCP,LSP,ISP,DIP。今天我就粗浅的说一下我对这几个原则的理解,请大家批评指正。
1. SRP——单一职责原则
这个是我认为在五大原则中最最重要的原则了,因为符合了它其他的原则都可以很轻松的办到。
单一职责是说每一个对象都有且仅有一个关键抽象。凡是这个关键抽象能力之外的事都由其他对象去完成。对象之间可以是使用关系或者聚合关系。
单一职责的对象因为接口不复杂,更容易被使用,而且不容易被误用。
构建对象时要特别避免全能对象和贫血对象。
全能对象是结构化编程思维的产物,很多由结构化编程思维转向面向对象语言时都会犯这种错误。整个程序由几个大而全的全能对象控制整个流程,强调的是动作的协调控制。这与面向对象的思维是相悖的。
贫血对象是指只有get和set的对象,没有有意义的动作,其实往往贫血对象和全能对象是共同存在的,原因就是全能对象把本应该由贫血对象完成的动作给抢去做了,所以造成了明明应该有关键抽象的对象,变成了无意义的贫血对象。
2. OCP——开放封闭原则
对扩展开放,对修改封闭
核心思想是让我们针对接口编程,不要基于实现编程。接口与实现分离。
提炼接口是需要对需求的精准把握的,经验不足的开发者经常无法提炼出优质的接口,一旦对需求的理解有偏差,势必需要修改接口,结果依然无法满足OCP原则。
而且,软件开发是一个需要不断重构的过程,“不过分设计”原则很重要,特别在需求还不是很清晰明确的时候。个人认为正确的原则应该是在真正需要接口和实现分离的时候才开始考虑OCP。
3. LSP——Liskov替换原则
这是一条关于继承的原则。我个人觉得应当尽量少的去使用继承,只在基类需求极度稳定,不会变动接口的情况下使用。否则一旦基类接口变动,带来的将可能是无比不灵活的代码。
这条原则讲的正是这个道理:子类必须能够完全替代基类才可以使用继承。语义上和接口上都要能完全替代。
而且子类最好不要实现什么新增功能,使用对象的对象也应当只知道基类类型比较好,子类最好是被隐藏,仅仅只做实现,任何时候不被用作接口。
如果真的想要达到功能代码复用,其实可以考虑采用类似于Scala里面的trait来完成(C++里面可以表达为继承或多重继承),当然也可以用组合来完成(委托给其他对象)
例子:正方形对象不能继承与长方形对象。(可参考Bob大叔的书)
4. ISP——接口隔离原则
这条原则说的是接口应当仅有一个关键抽象,即:接口的SRP原则。
Bob大叔的书中给出了一个例子:
要构建一个TimedDoor,现在有一个Door接口和一个TimerClient接口,如果让TimedDoor继承与Door接口,同时让Door接口继承于TimerClient接口是可以完成TimedDoor的构建,但是并非每个个Door都要依赖于TimedClient接口,所以这时的Door接口就不是符合ISP原则的。原因就是它不再满足SRP原则了。
5. DIP——依赖倒置原则
这就是大家经常说的依赖注入。基于接口编程是DIP的核心。
当要把两个逻辑上弱相关的对象(两个对象间是间接的使用关系)联系起来的时候,可以考虑使用DIP。
例如开关和灯这两个对象,开关可以控制灯,但开关和灯是弱相关的(开关不是一定要控制灯),如果直接让开关依赖于灯,那么这个开关只能用在灯上面了,以后如果想让这个开关控制一个电风扇就没办法了,必须再构建一个能控制电风扇的开关。
使用DIP原则后,可以让开关控制一个Switchable的接口,灯可以实现这个Switchable接口,电风扇也可以实现这个接口,这样就让开关不再依赖于灯了,就变得更通用了。
当然,如果你的程序里面只有灯会和开关相关联,那么直接让开关依赖灯也没什么,毕竟“不过分设计”是更高一些的原则。
说了这么多,本人觉得在遵循“不过分设计”的原则下,SRP原则是最重要的。其他原则固然也重要,但是在满足了SRP原则后,如果真的有必要,都是可以轻松达成的。
1. SRP——单一职责原则
这个是我认为在五大原则中最最重要的原则了,因为符合了它其他的原则都可以很轻松的办到。
单一职责是说每一个对象都有且仅有一个关键抽象。凡是这个关键抽象能力之外的事都由其他对象去完成。对象之间可以是使用关系或者聚合关系。
单一职责的对象因为接口不复杂,更容易被使用,而且不容易被误用。
构建对象时要特别避免全能对象和贫血对象。
全能对象是结构化编程思维的产物,很多由结构化编程思维转向面向对象语言时都会犯这种错误。整个程序由几个大而全的全能对象控制整个流程,强调的是动作的协调控制。这与面向对象的思维是相悖的。
贫血对象是指只有get和set的对象,没有有意义的动作,其实往往贫血对象和全能对象是共同存在的,原因就是全能对象把本应该由贫血对象完成的动作给抢去做了,所以造成了明明应该有关键抽象的对象,变成了无意义的贫血对象。
2. OCP——开放封闭原则
对扩展开放,对修改封闭
核心思想是让我们针对接口编程,不要基于实现编程。接口与实现分离。
提炼接口是需要对需求的精准把握的,经验不足的开发者经常无法提炼出优质的接口,一旦对需求的理解有偏差,势必需要修改接口,结果依然无法满足OCP原则。
而且,软件开发是一个需要不断重构的过程,“不过分设计”原则很重要,特别在需求还不是很清晰明确的时候。个人认为正确的原则应该是在真正需要接口和实现分离的时候才开始考虑OCP。
3. LSP——Liskov替换原则
这是一条关于继承的原则。我个人觉得应当尽量少的去使用继承,只在基类需求极度稳定,不会变动接口的情况下使用。否则一旦基类接口变动,带来的将可能是无比不灵活的代码。
这条原则讲的正是这个道理:子类必须能够完全替代基类才可以使用继承。语义上和接口上都要能完全替代。
而且子类最好不要实现什么新增功能,使用对象的对象也应当只知道基类类型比较好,子类最好是被隐藏,仅仅只做实现,任何时候不被用作接口。
如果真的想要达到功能代码复用,其实可以考虑采用类似于Scala里面的trait来完成(C++里面可以表达为继承或多重继承),当然也可以用组合来完成(委托给其他对象)
例子:正方形对象不能继承与长方形对象。(可参考Bob大叔的书)
4. ISP——接口隔离原则
这条原则说的是接口应当仅有一个关键抽象,即:接口的SRP原则。
Bob大叔的书中给出了一个例子:
要构建一个TimedDoor,现在有一个Door接口和一个TimerClient接口,如果让TimedDoor继承与Door接口,同时让Door接口继承于TimerClient接口是可以完成TimedDoor的构建,但是并非每个个Door都要依赖于TimedClient接口,所以这时的Door接口就不是符合ISP原则的。原因就是它不再满足SRP原则了。
5. DIP——依赖倒置原则
这就是大家经常说的依赖注入。基于接口编程是DIP的核心。
当要把两个逻辑上弱相关的对象(两个对象间是间接的使用关系)联系起来的时候,可以考虑使用DIP。
例如开关和灯这两个对象,开关可以控制灯,但开关和灯是弱相关的(开关不是一定要控制灯),如果直接让开关依赖于灯,那么这个开关只能用在灯上面了,以后如果想让这个开关控制一个电风扇就没办法了,必须再构建一个能控制电风扇的开关。
使用DIP原则后,可以让开关控制一个Switchable的接口,灯可以实现这个Switchable接口,电风扇也可以实现这个接口,这样就让开关不再依赖于灯了,就变得更通用了。
当然,如果你的程序里面只有灯会和开关相关联,那么直接让开关依赖灯也没什么,毕竟“不过分设计”是更高一些的原则。
说了这么多,本人觉得在遵循“不过分设计”的原则下,SRP原则是最重要的。其他原则固然也重要,但是在满足了SRP原则后,如果真的有必要,都是可以轻松达成的。
相关文章推荐
- javascript笔记之---Date类型
- Android Activity 、 Window 、 View之间的关系
- java学习笔记之:java对称加密
- window64下redis的安装与测试
- Oracle内连接、外连接、右外连接、全外连接 实现表关联显示
- 学习windwos驱动(IO模型)
- Medial Queries的另一用法——服务于IE
- 这7个人生捷径,一定不要走!
- 【linux shell系列--1】crontab命令
- property中的strong 、weak、copy 、assign 、retain 、unsafe_unretained 与autoreleasing区别和作用详解
- (转)C++实现RTMP协议发送H.264编码及AAC编码的音视频,摄像头直播
- 统计功能和子对象的大小信息查询Bug
- Mac电脑AndroidStudio使用SVN进行版本控制
- Android studio 导入project
- 获取本年的周六周日
- thinkphp——前台(提交订单)并插入相关信息到数据库
- 如何将JAVA插件支持Chrome
- 中文排序问题
- 【LWJGL官方教程】Game loops 游戏循环
- 腾讯封了超10万违规涉赌微信账号