当系统扩展遇到违背OO的里氏原则(LSP)的时候怎么办 ?
先转一篇写得很好的文章:http://www.cnblogs.com/CodeGuy/archive/2012/03/26/2418803.html
==================================================================
OO的五大原则是指SRP、OCP、LSP、DIP、ISP。
SRP -- (Single Responsibility Principle 单一职责原则)
OCP——开闭原则(Closed for Modification; Open for Extension)
OO设计的五大原则之间并不是相互孤立的。彼此间存在着一定关联,一个可以是另一个原则的加强或是基础。违反其中的某一个,可能同时违反了其余的原则。因此应该把这些原则融会贯通,牢记在心!
OO的五大原则是指SRP、OCP、LSP、DIP、ISP。
1. SRP(Single Responsibility Principle 单一职责原则)
单一职责很容易理解,也很容易实现。所谓单一职责,就是一个设计元素只做一件事。什么是“只做一件事”?简单说就是少管闲事。现实中就是如此,如果要你专心做一件事情,任何人都有信心可以做得很出色。
OCP作为OO的高层原则,主张使用“抽象(Abstraction)”和“多态(Polymorphism)”将设计中的静态结构改为动态结构,维持设计的封闭性。
2. OCP :开闭原则,很简单,一句话:“Closed for Modification; Open for Extension”——“对变更关闭;对扩展开放”。开闭原则其实没什么好讲的,我将其归结为一个高层次的设计总则。OCP的动机很简单:软件是变化的。不论是优质的设计还是低劣的设计都无法回避这一问题。OCP说明了软件设计应该尽可能地使架构稳定而又容易满足不同的需求。 为什么要OCP?答案也很简单——重用。
3.LSP——里氏替换原则
OCP作为OO的高层原则,主张使用“抽象(Abstraction)”和“多态(Polymorphism)”将设计中的静态结构改为动态结构,维持设计的封闭性“抽象”是语言提供的功能。“多态”由继承语义实现。 如此,问题产生了:“我们如何去度量继承关系的质量?”
Liskov于1987年提出了一个关于继承的原则“Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.”——“继承必须确保超类所拥有的性质在子类中仍然成立。”也就是说,当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is-A关系。
该原则称为Liskov Substitution Principle——里氏替换原则。
我们来研究一下LSP的实质。学习OO的时候,我们知道,一个对象是一组状态和一系列行为的组合体。状态是对象的内在特性,行为是对象的外在特性。LSP所表述的就是在同一个继承体系中的对象应该有共同的行为特征。
这一点上,表明了OO的继承与日常生活中的继承的本质区别。举一个例子:生物学的分类体系中把企鹅归属为鸟类。我们模仿这个体系,设计出这样的类和关系。
类“鸟”中有个方法fly,企鹅自然也继承了这个方法,可是企鹅不能飞阿,于是,我们在企鹅的类中覆盖了fly方法,告诉方法的调用者:企鹅是不会飞的。这完全符合常理。但是,这违反了LSP,企鹅是鸟的子类,可是企鹅却不能飞!需要注意的是,此处的“鸟”已经不再是生物学中的鸟了,它是软件中的一个类、一个抽象。
有人会说,企鹅不能飞很正常啊,而且这样编写代码也能正常编译,只要在使用这个类的客户代码中加一句判断就行了。但是,这就是问题所在!首先,客户代码和“企鹅”的代码很有可能不是同时设计的,在当今软件外包一层又一层的开发模式下,你甚至根本不知道两个模块的原产地是哪里,也就谈不上去修改客户代码了。客户程序很可能是遗留系统的一部分,很可能已经不再维护,如果因为设计出这么一个“企鹅”而导致必须修改客户代码,谁应该承担这部分责任呢?(大概是上帝吧,谁叫他让“企鹅”不能飞的。^_^)“修改客户代码”直接违反了OCP,这就是OCP的重要性。违反LSP将使既有的设计不能封闭!
修正后的设计如下:
LSP并没有提供解决这个问题的方案,而只是提出了这么一个问题。 于是,工程师们开始关注如何确保对象的行为。1988年,B. Meyer提出了Design by Contract(契约式设计)理论。DbC从形式化方法中借鉴了一套确保对象行为和自身状态的方法,其基本概念很简单:
每个方法调用之前,该方法应该校验传入参数的正确性,只有正确才能执行该方法,否则认为调用方违反契约,不予执行。这称为前置条件(Pre-condition)。
一旦通过前置条件的校验,方法必须执行,并且必须确保执行结果符合契约,这称之为后置条件(Post-condition)。
对象本身有一套对自身状态进行校验的检查条件,以确保该对象的本质不发生改变,这称之为不变式(Invariant)。
以上是单个对象的约束条件。为了满足LSP,当存在继承关系时,子类中方法的前置条件必须与超类中被覆盖的方法的前置条件相同或者更宽松;而子类中方法的后置条件必须与超类中被覆盖的方法的后置条件相同或者更为严格。
4.DIP 依赖倒置原则
依赖倒置(Dependence Inversion Principle)原则讲的是:要依赖于抽象,不要依赖于具体。
简单的说,依赖倒置原则要求客户端依赖于抽象耦合。原则表述:
抽象不应当依赖于细节;细节应当依赖于抽象;
要针对接口编程,不针对实现编程。
5.ISP 接口隔离原则
使用多个专门的接口比使用单一的总接口要好。广义的接口:一个接口相当于剧本中的一种角色,而此角色在一个舞台上由哪一个演员来演则相当于接口的实现。因此一个接口应当简单的代表一个角色,而不是一个角色。,如果系统设计多个角色的话,则应当每一个角色都由一个特定的接口代表。狭义的接口(Interface):接口隔离原则讲的就是同一个角色提供宽、窄不同的接口,以对付不同的客户端。
当系统扩展遇到违背OO的里氏原则(LSP)的时候怎么办 ?的更多相关文章
- 27-SQLServer系统扩展存储过程
一.注意点 1.在SQLServer中,有些系统扩展存储过程,是有风险,需要取消public角色的执行权限. 2.从SQLServer2005开始就不能通过sp_dropextendedproc 删除 ...
- 系统扩展与 macOS 不兼容
系统扩展与 macOS 不兼容 某些系统扩展与当前版本的 macOS 不兼容或将与后续 macOS 版本不兼容 https://support.apple.com/zh-cn/HT210999 ref ...
- OO的五大原则是指SRP、OCP、LSP、DIP、ISP。
OO的高层原则,面向对象设计的基本原则 设计模式之六大原则--开闭原则(OCP) 设计模式之六大原则--迪米特法则(LoD,LKP) 设计模式之六大原则--接口隔离原则(ISP) 设计模式之六大原则- ...
- OO的五大原则:SRP、OCP、LSP、DIP、ISP
OO的五大原则是指SRP.OCP.LSP.DIP.ISP. SRP -- (Single Responsibility Principle 单一职责原则) OCP--开闭原则(Closed for M ...
- OO设计原则 -- OO设计的原则及设计过程的全面总结
这部分增加一点自己的感想,OO设计原则下面讲述的很清晰;看完之后有点感想如果我们在实际开发当中能够把这些原则熟烂于心的话那我们的代码质量和个人能力会有很显著的提神.根据自己的实际经验看很多开发者在开发 ...
- OO的设计原则
今天同事和我们一起讨论分享了OO的设计原则,讨论使人明晰,有人一起讨论学习是一件幸福的事情. 1.开闭原则 对功能的扩展是开放的,对修改是闭合的. 可以应用于类的设计,框架的设计等. 为什么?开闭原则 ...
- OO的五大原则
OO的五大原则是指SRP.OCP.LSP.DIP.ISP 1. SRP(Single Responsibility Principle 单一职责原则) 单一职责很容易理解,所谓单一职责,就是一个设计元 ...
- .net学习之继承、里氏替换原则LSP、虚方法、多态、抽象类、Equals方法、接口、装箱拆箱、字符串
1.继承(1)创建子类对象的时候,在子类对象中会为子类对象的字段开辟空间,也会为父类的所有字段开辟空间,只不过父类私有的成员访问不到(2)子类从父类继承父类所有的非私有成员,但是父类的所有字段也会创建 ...
- 深入理解JavaScript系列(8):S.O.L.I.D五大原则之里氏替换原则LSP
前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第3篇,里氏替换原则LSP(The Liskov Substitution Principle ). 英文原文:http ...
随机推荐
- linux centOS7 设置 redis 开机启动
1.为了让redis-server能在系统启动时自动运行,需要将redis服务作为守护进程(daemon)来运行,我们回/usr/local/cluster/7000/目录中找到一个redis.con ...
- Oracle表字段的增删改和重命名
增加字段语法:alter table tablename add (column datatype [default value][null/not null],….); 说明:alter table ...
- MySQL SELECT语句中只能输出1000行数据的原因
同事反映,客户的一套MySQL生产库,执行SELECT.. INTO OUTFILE语句只能导出1000行 最初以为是系统参数被重新设置了,建议他更改系统参数 mysql> set global ...
- VS2017C++单元测试
0.欢迎食用 希望对点进来的你有所帮助. 全文记流水账,内心想法如示例项目名称. 1.建立需测试的项目 新建项目 正常书写.h 和.cpp文件 2.新建单元测试 右击解决方案 -> 添加 -&g ...
- Django商城项目笔记No.18商品部分-数据表创建
数据库表设计 在电商中对于商品,有两个重要的概念:SPU和SKU SPU = Standard Product Unit (标准产品单位) SPU是商品信息聚合的最小单位,是一组可服用.易检索的标准化 ...
- facebook api & oauth protocal
http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-10.5 http://stackoverflow.com/questions/14 ...
- Redis系列三:reids常用命令
全局命令 keys * 查看所有键 dbsize 查看的是当前所在redis数据库的键总数 如果存在大量键,线上禁止使用此指令 exists key 检查键是否存在,存在返回1,不存在返回0 del ...
- 爬虫代理squid
yum -y install squid 配置文件一般是在/etc/squid3/下的squid.conf文件 pyspider使用的时候只需要设置代理服务器为你配置好的服务器的IP就可以了 clas ...
- Python2.7-shelve
shelve模块,持久化对象数据,可以说是 pickle 模块的封装,用于把像字典一样的以键-值存储的数据持久化存储,像操作数据库.当我们写程序的时候如果不想用关系数据库那么重量级的东东去存储数据,不 ...
- Python2.7-pickle, cpickle
pickle, cpickle模块,用于序列化和反序列化 python 对象数据,可以被序列化的有:布尔值,数值,字符串,包含以上三类的容器,定义在模块顶层的函数.内置函数和类,实例对象的 __dic ...