前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式(二):里氏替换原则

设计模式(二):里氏替换原则

原创
作者头像
xujjj
修改2019-07-01 10:20:51
5700
修改2019-07-01 10:20:51
举报

什么是里氏替换原则?

定义:所有引用父类的地方必须能透明地使用其子类的对象。

为什么要有里氏替换原则?

里氏替换原则主要阐述了关于面向对象继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理。里氏替换原是继承复用的基础,它反映了父类与子类之间的关系。

里氏替换原则具有以下优点:

  • 为良好的继承定义了一个规范;
  • 提高代码的健壮性,降低程序出错的可能性;

里氏替换原则的四个原则

1、子类必须实现父类的抽象方法,但不得重写父类的非抽象方法

举例说明重写父类的非抽象方法的后果

可见 Ostrich 鸵鸟类继承 Bird 鸟类,并重写 fly 方法,然而 Ostrich 类的 fly 方法已经和父类不一致了,鸵鸟不会飞。对于使用者来说,他们原先是不知道鸵鸟会不会飞的,只有在使用的时候才知道,这样就会导致难以预料的后果,比如要计算飞行时间,根据 t = s / v 公式,鸵鸟的飞行速度为 0 ,就会抛出异常了。

2、子类可以有自己的方法

子类继承了父类,除了拥有了父类的属性和方法外,还可以自定义属性和方法,这是对父类功能的拓展,符合里氏替换原则。

上面代码 啄木鸟 类不但拥有父类的飞功能,还有啄木的技能。所以凡是鸟父类出现的地方,都可以使用啄木鸟类替换,反过来就不行,因为鸟父类没有啄木的技能。从里氏替换原则来看,就是有子类出现的地方父类未必就可以出现。

3、当子类覆盖或实现父类的方法时,方法的输入参数可以比父类方法的输入参数更宽松

父类方法的输入形参类型为 T,子类方法输入形参类型为 S,那么根据里氏替换原则就要求 S 必须大于等于 T。也就是说,要么 S 和 T 是同一类型,要么 S 是 T 的父类。

我们可以看到,因为父类 fly 方法参数类型 HashMap 是 子类 fly 方法参数类型 Map 的子类(符合T <=  S),所以上述程序无论是父类 A 还是子类 B 都是正常执行父类 A 的方法,符合里氏替换原则。

那假如写一段 T > S 的程序会怎么样,我们一起来看看:

我们可以看到,因为父类 fly 方法参数类型 Map 是 子类 fly 方法参数类型 HashMap 的父类(符合T >  S),父类正常执行自己的 fly 方法输出,当将父类的位置替换成子类时,输出结果不一致,违反了里氏替换原则中 父类出现的地方子类也能出现的概念,使得程序出现不便排查的错误。

4、当子类覆盖或实现父类的方法时,方法的返回结果可以比父类方法的返回结果范围更严格

父类方法的返回类型为 T,子类的返回类型为 S,那么根据里氏替换原则就要求 S 必须小于等于 T。也就是说,要么S和T是同一类型,要么 T 是 S 的父类。

我们可以看到,因为父类 fly 方法返回类型 Map 是 子类 fly 方法参数类型 HashMap 的父类(符合T >  S),可以正常输出,符合里氏替换原则。若 T <= S 则编译器就已经报错了,不可能执行,具体各位读者可自行验证!

总结

除了遵守以上四个规则外,里氏替换原则也建议我们应当尽可能将父类保持简单化和最小化,这样有助于子类的拓展性。因为如果一个父类是比较复杂的,那么子类在继承并拓展它的时候,要在不影响父类状态语义的情况下进行扩展是一件比较困难的事情。

以上就是今天《里氏替换原则》的讲解,良好的代码风格需要长期不断的积累学习。各位读者大人若有问题,欢迎后台留言,我将第一时间回复!

下期文章将介绍《设计模式(三):依赖倒置原则》

欢迎关注我们的微信公众号:IT界的泥石流

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是里氏替换原则?
  • 为什么要有里氏替换原则?
  • 里氏替换原则的四个原则
  • 总结
    • 欢迎关注我们的微信公众号:IT界的泥石流
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档