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面向对象设计基础》
分享到:
相关推荐
从网上摘抄下来的一份设计良品,杜绝垃圾代码!
面向对象设计原则和设计模式的概念,讲述面向对象的设计模式
面向对象,编程,OO设计的五大原则 OO的五大原则是指SRP、OCP、LSP、DIP、ISP。
《面向对象分析与设计(第3版)》是UML创始人Grady Booch的代表作之一,书中介绍的概念都基于牢固的理论基础。同时,《面向对象分析与设计(第3版)》又是一本注重实效的书,面向架构师和软件开发者等软件工程实践者的...
掌握内聚度和耦合度的概念 掌握面向对象设计原则
面向对象设计原则,doc格式,方便编辑整理。
第一册:《Head First Java》是本完整的面向对象(object-oriented,OO)程序设计和Java的学习指导。 第二册:《HeadFirst设计模式》(中文版)共有14章,每章都介绍了几个设计模式,完整地涵盖了四人组版本全部23个设计...
主要讲解面向对象的设计原则,在面向对象设计中,如何通过很小的设计改变就可以应对设计需求的变化,这是令设计者极为关注的问题。为此不少OO先驱提出了很多有关面向对象的设计原则用于指导OO的设计和开发
一些面向对象的设计原则,写了多年代码别说你还不知道这些原则啊╮(╯_╰)╭ 学设计模式前最好看看这个,高清晰带书签pdf
面向对象设计根本的指导原则是提高可维护性和可复用性。这些原则主要有:1. 开闭原则;2. 依赖倒转原则;3. 里氏代换原则;4. 合成/聚合复用原则;5. 迪米特原则;6. 接口隔离原则
面向对象的类的设计原则,本人认为对于学习面向对象的设计很有用。开放关闭原则
使用通俗的举例让大家知道oo的概念OO 面向对象概念通俗说明
面向对象设计的主要任务就是类的设计,不少面向对象(OO)的先驱和前辈已经提出了很多关于类的设计原则,用于指导OOP,其中就包括类设计的五项基本原则。 1.单一职责原则(Single Resposibility Principle,SRP) ...
强烈推荐 ... 通过一连串的脑力开发,《深入浅出面向对象分析与设计》压缩了学习与获取复杂信息所需的时间。可以预料,这将是一段充满乐趣的学习之旅。相信在读完本书之时,你肯定能够写出伟大的软件。
OO真经—关于面向对象的哲学体系及科学体系的探讨
面向对象(Object Oriented,OO),注重的是一个个的对象,这些对象各司其职,我们只需发号施令,即可指挥这些对象帮我们完成任务。 6.1.1 面向过程与面向对象 1、概念 2、面向过程与面向对象的优缺点 优点 缺点 ...
OO设计原则 --OO设计的LSP里氏替换原则 ,所以使用基类代码的地方,用派生类代码替换后,能够正确的执行动作处理。 ◇ 换句话说,如果派生类替换了基类后,不能够正确执行动作,那么他们的继承关系就应该废除。
相关的数据和逻辑形成个体,这些个体叫做对象(Object),世界就是由一个个对象组成的。对象具有相对独立性,对外提供一定的服务。所谓世界的演进,是在某个“初始作用力”作用下,对象间通过相互调用而完成的交互;...
强烈推荐 ... 通过一连串的脑力开发,《深入浅出面向对象分析与设计》压缩了学习与获取复杂信息所需的时间。可以预料,这将是一段充满乐趣的学习之旅。相信在读完本书之时,你肯定能够写出伟大的软件。