`
王杲杲
  • 浏览: 43598 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

“学习OO好榜样”之面向对象设计原则

阅读更多

Open-Close原则
是一个愿景性质的原则,如果系统能够达到Open-Close原则描述的情形就比较理想了,对扩展开放、对修改关闭,即,不修改原有代码即可完成对系统的扩展。
实现Open-Closed原则,抽象化是关键。
抽象层,因为抽象所以稳定。不变应万变,不用修改,满足Open-Closed原则的Closed一头。
抽象层的具体实现层可以满足扩展要求,满足Open-Closed原则的Open一头。
Open-Closed原则还可表述为“对可变性的封装”原则。“找到一个系统的可变因素,将它封装起来。”
一个可变性因素,不应该被散落在各个角落,而应该被封装到一个对象中。
一种可变性因素,不应该与另一种可变性因素混和在一起,而应各自独立开。
但,单从原则本身来说,它并没有告诉我们如何才能够让系统满足这个原则。

里氏代换原则
指导我们如何去构建一个extends(继承、派生)结构。
子类与父类的关系必须是is-A,即,子类必须在任何场合都敢于大声宣称自己起码(至少)是一个父类。比如,假设某类结构,“男人”、“女人”从“人”派生出来,看起来就是满足里氏代换原则的,因为无论“男人”还是“女人”,在任何场合都是“人”。这个原则大多数情况下,可以用现实世界中的概念来思考,但软件世界与显示世界毕竟有区别,比如,书中的例子,“正方形是不是矩形”问题。
此外,很多问题需要利用OO核心思想来灵活考虑,还是书中的例子(我只说大概意思,可能与书中描述存在差异),一个类结构,“职工、普通职员、项目经理、科长、部长……”,从“员工”类派生出来,从一般概念来看还不错,职工和部长都是员工嘛,但这里隐含问题,现实世界中,普通职员可能变成项目经理,但软件世界中,普通职员和项目经理被规划成两个类了,于是一个普通职员instance是很难变成项目经理instance的,这说明我们把显示世界映射为软件世界时,出现了问题。更加合理的做法是,把这些职务(角色)抽象出来,比如叫做“职务”类(interface/abstract class),所有职务从其派生,员工类、职务类是关联关系,任何一个员工实例都一个职务实例作为它的属性,这样就对了,员工的职务是可以变化的。实现这个重构的依据,是OO中的封装变化思想,以及从中演化出来的依赖倒换原则、合成/聚合复用原则。
但凡涉及到extends结构的设计模式都符合着里氏代换原则:
策略模式:一组算法,把他们封装成对象,使之可以互换(满足同一接口,即都is-A这个接口)。
合成模式:Leaf和Composite都is-A Component,于是才可能方便地层层嵌套。
代理模式:Proxy与RealSubject都is-A父类Subject,于是可以插入代理,完成附加功能。
……

依赖倒换原则
很具体地指导我们对抽象类(接口)、实现类的使用。
依赖于抽象的实体(interface/abstract class),才能够更具有可插入性(但凡实现既有接口的实现类实例都可以在依赖此接口的地方以此接口实例的角色插入进来),更容易满足Open-Close原则(抽象的层次不变化、实现的层次由于使用不同的类来封装不同的变化,于是可以在增加新类作为扩展的同时不需要修改已有实现类)。

接口隔离原则
这个原则比较孤立,也是从OO封装变化思想演化出来的,要将变化封装到最“贴身”。比如,A/B接口可能在大多数情况下都由同一个实现类提供,但某些情况下,B接口都有些实现类是没有意义的,这时候就需要把A/B分开作为两个接口,某些类实现全部A/B接口、某些类仅实现A接口。A/B单独变化,于是把A/B单独封装,接口隔离。

合成/聚合复用原则
与里氏代换原则关系较为紧密,里氏代换原则告诉我们什么情况下使用extends,在不满足里氏代换原则时,还想达到重用的目的,就引出合成/聚合复用原则。上文中,员工与职务的例子,来说明这个事儿就比较合适。
从复用角度来说:“合成/聚合复用”比“继承”复用灵活。前者是动态复用(因而具有可插入性)、后者是静态复用(编译时就固定了复用关系),而且后者的复用有“不支持多重继承”的限制。

迪米特法则
用于解开类之间的不必要的耦合。“不要与陌生人说话”。说起来容易,实际操作的时候很可能出现些无所适从的问题,而解开类之间的耦合是比较重要的,目前我们设计类的时候,增加属性、增加对其他类的调用都比较随意,不太好。
迪米特法则要求:一个对象应当对其他对象有尽可能少的了解。几种其他的表述:只与你直接的朋友通信、不要跟“陌生人”说话(冯远征?)
每个软件单位对其他单位都具有最少的知识,而且局限于那些与本单位密切相关的软件单位。
狭义迪米特法则-规定了谁是Friends、谁是Stranger。Friends圈子包括:
当前对象自身(this)、Instance field(如果field是Map、List等容器类型,则容器内的对象也是朋友)、当前对象创建的对象(调用其构造方法)、当前对象方法的参数对象。
只跟Friends说话,跟Stranger说的话由Friends转述。
狭义迪米特法则的弊端:传递间接调用的小方法太多!解决办法:遵循依赖倒换原则做些折衷处理,让对象依赖于Stranger的抽象层。虽然没有完全断开耦合,至少降低了耦合。

小结
Open-Close原则是比较总体的要求、是愿景。
实现Open-Close原则的关键,是抽象。
依赖倒换原则就是强调抽象的。
有了抽象层,就要有实现层,实现层无疑要从抽象层extends/implements,实现层/抽象层的关系,要依从里氏代换原则。
很多情况不满足里氏代换原则,又要实现复用,于是引出,合成/聚合复用原则。
两外两个原则是说类之间(接口)关系的。
接口隔离原则,要求类之间的接口更加狭窄,更加确切、更加合身地封装变化。
迪米特法则,要求类之间的接口应该发生在哪些类之间,不应该发生在哪些类之间。帮助我们解开类之间的耦合。

参考文献:
阎宏《Java与模式》
Meilir Page-Jones《UML面向对象设计基础》

分享到:
评论
6 楼 spiritfrog 2007-05-24  
都是理论的东西了,能不能结合实际的说一说呢?
5 楼 liudong9183 2007-05-23  
抄书.........
好像在java与设计模式中看过..
4 楼 wuhua 2007-05-23  
什么OO,什么模式,XXX,都得自己做了才有个谱
3 楼 liuganquan 2007-05-23  
楼上正解,要能够在实际的应用中才能理解设计原则,设计原则也是为了应对变化,提高代码重用
2 楼 shaucle 2007-05-23  
实践第一,不然这些思想很难凭空体会
1 楼 sniperking 2007-05-23  
真正在实际中能做到OO确实是个很难的事情

相关推荐

Global site tag (gtag.js) - Google Analytics