下面以“几维鸟不是鸟”为例来说明里氏替换原则(程序源码)。

#include <QCoreApplication>
#include <iostream>/*!* \brief 鸟类*/
class Bird{
public:double _fly_speed;void SetSpeed(double speed){_fly_speed = speed;}double GetFlyTime(double distance){return (distance/_fly_speed);}
};/*!* \brief 燕子类*/
class Swallow:public Bird{};/*!* \brief 几维鸟类*/
class BrownKiwi:public Bird{
public:void SetSpeed(double speed){_fly_speed = 0;}
};int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);Swallow *bird1 = new Swallow;bird1->SetSpeed(120);std::cout<<bird1->_fly_speed<<std::endl;std::cout<<bird1->GetFlyTime(300)<<std::endl;BrownKiwi *bird2 = new BrownKiwi;bird2->SetSpeed(120);std::cout<<bird2->_fly_speed<<std::endl;std::cout<<bird2->GetFlyTime(300)<<std::endl;return a.exec();
}

运行结果:

120
2.5
0
inf

分析:鸟一般都会飞行,如燕子的飞行速度大概是每小时 120 千米。但是新西兰的几维鸟由于翅膀退化无法飞行。假如要设计一个实例,计算这两种鸟飞行 300 千米要花费的时间。显然,拿燕子来测试这段代码,结果正确,能计算出所需要的时间;但拿几维鸟来测试,结果会发生“除零异常”或是“无穷大”,明显不符合预期,其类图如图 1 所示。


图1 “几维鸟不是鸟”实例的类图

程序运行错误的原因是:几维鸟类重写了鸟类的 setSpeed(double speed) 方法,这违背了里氏替换原则。

解决方法:

取消几维鸟原来的继承关系,定义鸟和几维鸟的更一般的父类,如动物类,它们都有奔跑的能力。几维鸟的飞行速度虽然为 0,但奔跑速度不为 0,可以计算出其奔跑 300 千米所要花费的时间。其类图如图 2 所示。

#include <QCoreApplication>
#include <iostream>/*!* \brief 动物类*/
class Animal{
public:double _run_speed;void SetRunSpeed(double speed){_run_speed = speed;}double GetRunTime(double distance){return (distance/_run_speed);}
};/*!* \brief 鸟类*/
class Bird:public Animal{
public:double _fly_speed;void SetSpeed(double speed){_fly_speed = speed;}double GetFlyTime(double distance){return (distance/_fly_speed);}
};/*!* \brief 燕子类*/
class Swallow:public Bird{};/*!* \brief 几维鸟类*/
class BrownKiwi:public Animal{};int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);Swallow *bird1 = new Swallow;bird1->SetSpeed(120);std::cout<<bird1->_fly_speed<<std::endl;std::cout<<bird1->GetFlyTime(300)<<std::endl;BrownKiwi *bird2 = new BrownKiwi;bird2->SetRunSpeed(120);std::cout<<bird2->_run_speed<<std::endl;std::cout<<bird2->GetRunTime(300)<<std::endl;return a.exec();
}

运行结果:

120
2.5
120
2.5

里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

根据上述理解,对里氏替换原则的定义可以总结如下:

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
  • 子类中可以增加自己特有的方法
  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松
  • 当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的的输出/返回值)要比父类的方法更严格或相等

参考

  1. 设计模式六大原则(2):里氏替换原则
  2. 里氏替换原则——面向对象设计原则

深入浅出设计模式原则之里氏代换原则(Liskov Substitution Principle)相关推荐

  1. 设计模式-设计原则之里氏代换原则

    设计原则之里氏代换原则 里氏代换原则 案例(正方形不是长方形) 案例改进 里氏代换原则 里氏代换原则是面向对象设计的基本原则之一. 里氏代换原则:任何基类可以出现的地方,子类一定可以出现. 通俗理解: ...

  2. 带你认识六种设计原则(开闭原则、里氏代换原则、依赖倒转原则....)

    前言 1. 设计原则 1.1. 开-闭原则 1.2. 里氏代换原则 1.3. 依赖倒转原则 1.4. 接口隔离原则 1.5. 合成/聚合原则 1.6. 迪米特法则 前言 学习设计模式之前先要了解其中的 ...

  3. Java设计原则之单一职责原则、开闭原则、里氏代换原则

    文章目录 面向对象设计原则概述 单一职责原则 开闭原则 里氏代换原则 面向对象设计原则概述 软件的可维护性(Maintainability)和可复用性(Reusability)是两个非常重要的用于衡量 ...

  4. 设计原则 单一职责原则、开放封闭原则、依赖倒置原则、里氏代换原则、迪米特法则

    目录 1 单一职责原则 2 开放封闭原则 3 依赖倒置原则 4 里氏代换原则 5 迪米特法则 1 单一职责原则 比如:电脑内存坏了就应该更换内存,不应该更换CPU (内存负责内存.CPU负责CPU) ...

  5. 依赖倒转原则和里氏代换原则详解

    初学依赖倒转原则和里氏代换原则时,由于笔者水平有限,并没有看懂书上的专业术语的解释,经过反复摸索和学习,发现里氏代换原则和依赖倒转原则可以一言以蔽之: 里氏代换原则:开发时以抽象为核心,针对抽象编程, ...

  6. 大话设计模式三之单一职责原则、开放-封闭原则、依赖倒置原则、里氏代换原则

    单一职责原则 单一职责原则(SRP),意思就是说,功能要单一.准确解释是,就一个类而言,应该仅有一个引起它变化的原因. 如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或 ...

  7. Java设计模式之设计的6大原则(开闭原则,里氏代换原则,依赖倒转原则,接口隔离原则,最少知道原则,合成复用原则)

    1. 开闭原则 核心思想:一个对象对外扩展开发,对修改关闭 意思就是:对类的改动是通过增加代码进行的,而不是修改现有的代码. 也就是说软件开发人员一旦写出了可以运行的代码,就不应该去改动它,而是要保证 ...

  8. 【设计模式系列学习笔记】5、依赖倒转原则和里氏代换原则

    依赖倒转原则:抽象不应该依赖细节,细节应该依赖于抽象: 针对接口编程,不要对实现编程: 高层模块不应该依赖底层模块,两个都应该依赖抽象: 抽象不应该依赖细节,细节应该依赖抽象: 里氏代换原则 一个软件 ...

  9. 系统设计原则之里氏代换原则

    之前讲述的"开-闭"原则是系统设计的主原则,做到这点是一件很不容易的工作.但是也不是高不可攀的,除此原则以外还有其他的一些设计原则为实现或者说尽可能的达到"开-闭&quo ...

  10. 面向对象设计原则之里氏代换原则

    里氏代换原则由2008年图灵奖得主.美国第一位计算机科学女博士Barbara Liskov教授和卡内基·梅隆大学Jeannette Wing教授于1994年提出.其严格表述如下:如果对每一个类型为S的 ...

最新文章

  1. JavaScript小记
  2. 是男人就下100层【第一层】——高仿微信界面(4)
  3. selenium+python自动化测试系列(一):登录
  4. 实现软件自动启动代码
  5. lisp 焊接符号标注_焊接符号标注大全
  6. python实现键盘记录器
  7. 【Java语法】DateFormat时间格式转化、java.util.Date和java.sql.Date之间的相互转化
  8. oracle按时间点还原数据
  9. ultra edit ftp帐号管理导入导出方法
  10. CentOS远程监控
  11. 组合数学 —— 基本计数原理
  12. kafka数据可靠传输
  13. mapview | 如何快速使用交互式地图展示空间数据信息
  14. 优秀的模糊测试代码是如何炼成的?
  15. 大数据平台层级架构图
  16. POJ 1053 Set Me G++
  17. 集成稳压电源的分类及特性
  18. java算法2——费氏数列
  19. 最佳Android模拟器,你值得拥有
  20. 100个常用的 PHP 类库、资源和技巧小结

热门文章

  1. wxWidgets:键码KeyCodes
  2. boost::mpl模块实现upper_bound相关的测试程序
  3. boost::mpl模块实现has_xxx相关的测试程序
  4. boost::make_maximal_planar用法的测试程序
  5. Boost::Flyweight 基本示例
  6. Boost::context模块fiber的解析器测试程序
  7. VTK:图片之Transparency
  8. VTK:图片之ImageGradientMagnitude
  9. VTK:Filtering之Delaunay2D
  10. OpenCV SURF FLANN匹配的实例(附完整代码)