草庐IT

设计模式七大原则—里氏替换原则

坚持 2023-03-28 原文

1.基本介绍

里斯科瓦(Barbara Liskov)使美国麻省理工学院电气工程于计算机科学系资深教授,她是美国国家工程院院士,在程序语言、分布式计算、程序设计方法及软件工程领域做出了卓越贡献。里斯科瓦于1987年提出了一个关于继承的原则,也就是现在我们称为的“里氏替换原则”。

里氏替换原则基于子类对象可以赋给父类对象的“多态性”,表明子类可以替换父类,并且出现在父类能够出现的任何地方。但是该原则要求继承时需要遵守以下两点:

  • 继承必须确保父类所拥有的“性质”在子类中仍然成立,且子类的程序执行无异常。
  • 子类可以新增特有方法,并且可以重写父类的抽象方法,但不能改变父类原有的方法。

这两点也主要是衡量继承的使用是否符合了里氏替换原则的标准。


 2.继承的滥用

通过一段小故事来讲解,为了让程序达到复用效果,滥用继承且不遵守“里氏替换原则”从而使程序带来意想不到的麻烦。

张三在学习了面向对象的技术后,深刻的感受到继承复用带来的快感,于是使用面向对象技术开发了一款“模拟鸭子游戏”,游戏中会出现各种鸭子,我们可以操作不同的鸭子进行游泳戏水或是呱呱叫。因为它采用了继承的特性,设计了一个鸭子父类,并让不同种类的鸭子都继承此父类。

随着游戏市场的竞争压力加剧,张三为了让自己的“模拟鸭子游戏”在市场上更受欢迎,张三开发扩展了一个新的功能,就是让游戏中所有鸭子可以飞行。此时的张三已经被继承的魅力冲昏了头脑,他想到鸭子都有翅膀,只需要在Duck类中加上Fly()方法,基于继承的特性所有鸭子都会复用这个Fly()方法,他心里想:“妙啊,这可真是一招定乾坤啊”。

“模拟鸭子游戏”在加入飞行功能投入市场后,可怕的问题发生了。某个玩家将一只“橡皮鸭”既然操作飞了起来,这显然不符合一个程序的正常逻辑,因此张三受到了公司的严厉制裁,不仅游戏被下架还被罚款。这是怎么回事?张三为了让程序整体达到复用效果,忽略了极个别的鸭子其实并不具备飞行的能力,这使得某些并不适用该行为(飞行)的子类,也具有了该行为。

这个故事告诉了我们,为了让程序达到复用的效果,继承并不是一个“以逸待劳”的方案,使用继承来达到复用必须要求继承的成员在所有子类中均有统一性。张三之所以没有使用好继承,就是因为他使用的继承过于片面,且没有遵守里氏替换原则。然而,理解里氏替换原则的最终目的,就是促使我们更好的、规范的使用继承,能够明白什么时候什么情况下正确的使用继承,以及其中蕴含的原理。


3.让继承性质合理化

接着张三开发“模拟鸭子游戏”的故事例子,我们来针对其中出现的问题:“即在使用继承过程中,为父类加入新的行为,从而导致某些并不适合该行为的子类,也被迫继承该行为。”来通过UML图的方式,介绍两种如何解决问题的方式。当然方式不止两种,但我们最终目的是要让我们的继承符合“里氏替换原则”。

1.切断不合理继承,扩展新的父类。

设计思路:从Duck类中去除了Fly方法,以此切断了橡皮鸭继承Fly方法的途径,然后新增“动物鸭”类包含Fly的方法,将会飞的鸭子继承之该类,并且可以间接继承Duck类中的共有特性(叫、游泳)。

 

2.组合方式

设计思路:该方式的做法和“继承”不同的地方在于,“Fly方法”不是继承来的,而是通过一个接口“组合”来的。子类可以根据自身情况实例化不同的“飞行接口实现类”来执行相应的飞行功能。该方式相对于第一种较为复杂,因为基本上已经形成了一个设计模式“策略模式”,在该章节对该模式不进行详细介绍,目前只用知道有这种方式即可,后续在对于的专题会详细展开。

有关设计模式七大原则—里氏替换原则的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  4. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  5. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  6. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  7. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  8. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  9. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  10. ruby - Ruby gsub 替换中的行为不一致? - 2

    两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio

随机推荐