先转一篇写得很好的文章: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)

现将近期整理的文档提供给大家,这里对LSP做重点的介绍,望对大家有帮助,在学习和使用OO设计的时候,我们应该明白:OO的出现使得软件工程师们能够用更接近真实世界的方法描述软件系统。然而,软件毕竟是建立在抽象层次上的东西,再怎么接近真实,也不能替代真实或被真实替代。 
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):接口隔离原则讲的就是同一个角色提供宽、窄不同的接口,以对付不同的客户端。

============================================================================
 
文中举例企鹅继承鸟这个问题,我觉得很有意思。确实如果鸟类中有fly()这个方法, 如果企鹅去继承了, 必然会飞不起来,鸵鸟也一样。但是我觉得到底企鹅能不能继承鸟类, 完全取决于对“鸟”的定义, 如果在应用中定义的“鸟”一定是会飞得起来的鸟, 那企鹅,鸵鸟肯定不能继承, 因为它们虽然都有翅膀, 但却都飞不起来。那问题出在哪里? 应该如何解决这个问题? 不能简单下结论,完全取决于应该本身的需求。
 
如果把系统当作一个鸟的乐园:
1. 最开始由于乐园里面的鸟都会飞, 所以基类“鸟”都有fly()方法。
2. 突然有一天,乐园领导让新鸟引进部门引进新的鸟进来,办事员由于不知道企鹅不会飞, 结果他把企鹅给收了,相当于企鹅继承了鸟这个类, 自然就有了fly()方法。 但企鹅却不会飞,于是乎,鸟乐园里面就有了一只不会飞的企鹅。
3. 那问题出在哪里? 有几种可能
   1)办事员办事不力, 本来不该引进企鹅, 结果由于知识缺乏,错误的引进了不会飞的企鹅。这个BUG的根源就是“新鸟引进部门”的错误导致的。
   2)同时, 虽然“新鸟引进部门”有问题,但原来乐园的名字也有问题,既然只能养会飞的鸟,那乐园名字就不该叫鸟乐园,而该叫”飞鸟乐园“, 乐园里面所有鸟的基类也应该叫”飞鸟“, 而不是”鸟“。
   3)鸟乐园本来就有不会飞的鸟,里面原来就有养鸵鸟,引入不会飞的企鹅完全没有问题。那么这个问题其实就是鸟这个基类定义错了, fly()这个方法本来就不该出现在“鸟”这个基类里面。
   4)鸟乐园原来确实只有会飞的鸟,但名字也没错,虽然现在全是会飞的鸟,但乐园领导最初的想法就是为了以后啥鸟都养。那现在引入企鹅后应该咋办呢, 就需要对系统做适当的重构,先把目前的"鸟"这个基类改名成“会飞的鸟”, 然后再构建一个“不会飞的鸟”, 里面没有fly()方法,在从飞“不会飞的鸟中”继承产生“企鹅”类。原来的“鸟”类去掉fly()方法,新的“会飞的鸟”与“不会飞的鸟”都可以继承自新定义的“鸟"类。

当系统扩展遇到违背OO的里氏原则(LSP)的时候怎么办 ?的更多相关文章

  1. 27-SQLServer系统扩展存储过程

    一.注意点 1.在SQLServer中,有些系统扩展存储过程,是有风险,需要取消public角色的执行权限. 2.从SQLServer2005开始就不能通过sp_dropextendedproc 删除 ...

  2. 系统扩展与 macOS 不兼容

    系统扩展与 macOS 不兼容 某些系统扩展与当前版本的 macOS 不兼容或将与后续 macOS 版本不兼容 https://support.apple.com/zh-cn/HT210999 ref ...

  3. OO的五大原则是指SRP、OCP、LSP、DIP、ISP。

    OO的高层原则,面向对象设计的基本原则 设计模式之六大原则--开闭原则(OCP) 设计模式之六大原则--迪米特法则(LoD,LKP) 设计模式之六大原则--接口隔离原则(ISP) 设计模式之六大原则- ...

  4. OO的五大原则:SRP、OCP、LSP、DIP、ISP

    OO的五大原则是指SRP.OCP.LSP.DIP.ISP. SRP -- (Single Responsibility Principle 单一职责原则) OCP--开闭原则(Closed for M ...

  5. OO设计原则 -- OO设计的原则及设计过程的全面总结

    这部分增加一点自己的感想,OO设计原则下面讲述的很清晰;看完之后有点感想如果我们在实际开发当中能够把这些原则熟烂于心的话那我们的代码质量和个人能力会有很显著的提神.根据自己的实际经验看很多开发者在开发 ...

  6. OO的设计原则

    今天同事和我们一起讨论分享了OO的设计原则,讨论使人明晰,有人一起讨论学习是一件幸福的事情. 1.开闭原则 对功能的扩展是开放的,对修改是闭合的. 可以应用于类的设计,框架的设计等. 为什么?开闭原则 ...

  7. OO的五大原则

    OO的五大原则是指SRP.OCP.LSP.DIP.ISP 1. SRP(Single Responsibility Principle 单一职责原则) 单一职责很容易理解,所谓单一职责,就是一个设计元 ...

  8. .net学习之继承、里氏替换原则LSP、虚方法、多态、抽象类、Equals方法、接口、装箱拆箱、字符串

    1.继承(1)创建子类对象的时候,在子类对象中会为子类对象的字段开辟空间,也会为父类的所有字段开辟空间,只不过父类私有的成员访问不到(2)子类从父类继承父类所有的非私有成员,但是父类的所有字段也会创建 ...

  9. 深入理解JavaScript系列(8):S.O.L.I.D五大原则之里氏替换原则LSP

    前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第3篇,里氏替换原则LSP(The Liskov Substitution Principle ). 英文原文:http ...

随机推荐

  1. Python+Selenium笔记(六):元素定位

      (一)  前言 Web应用以及包含超文本标记语言(HTML).层叠样式表(CSS).JS脚本的WEB页面,基于用户的操作(例如点击提交按钮),浏览器向WEB服务器发送请求,WEB服务器响应请求,返 ...

  2. 美图DPOS以太坊教程(Docker版)

    一.前言 最近,需要接触区块链项目的主链开发,在EOS.BTC.ethereum.超级账本这几种区块链技术当中,相互对比后,最终还是以go-ethereum为解决方案. 以ethereum为基准去找解 ...

  3. EFCore中SQLSERVER 2008 的分页问题

    自SQLSERVER 2012起新增了 Offset Fetch 语法,因此EFCore默认是以此语法生成相应的分页语句的. 如果我们的目标数据库低于 2012,那么EFCore默认生成的语句在执行的 ...

  4. Grafana是一个可视化面板-安装配置介绍

    Grafana是一个可视化面板(Dashboard),有着非常漂亮的图表和布局展示,功能齐全的度量仪表盘和图形编辑器,支持Graphite.zabbix.InfluxDB.Prometheus和Ope ...

  5. 500.19 ,错误:4.00x80070005

    直接把网站根目录添加上everyone权限即可

  6. ulimit linux文件配置

    文件描述符在形式上是一个非负整数.实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表.当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符.在程序设计中,一 ...

  7. Matlab feval函数(转)

    http://zhidao.baidu.com/link?url=7CusQYQXhCDB8sUtolMEhI1ctnpblbYrpSnU0fhIh5LvDZuhsBuozQusS6Kb1McTp7x ...

  8. WaitForMultipleObjects

    WaitForMultipleObjects是Windows中的一个功能非常强大的函数,几乎可以等待Windows中的所有的内核对象 函数原型为: DWORD WaitForMultipleObjec ...

  9. ndroid动态创建按钮并添加事件

    public class MyActivity extends Activity { /** * Called when the activity is first created. */ @Over ...

  10. python3: 迭代器与生成器(1)

    1. 手动遍历迭代器 你想遍历一个可迭代对象中的所有元素,但是却不想使用for循环. >>> items = [1, 2, 3] >>> # Get the ite ...