〇、小故事
我们怎么识别一辆汽车是宝马品牌的汽车呢?虽然宝马汽车车辆型号非常的多,而且外型也各不相同,但是只要是宝马品牌的汽车,它的车头一定会有宝马汽车的logo,那么这个就是大家最直观去确认一辆车是不是宝马牌汽车的主要依据了。
所以,宝马公司就跟各地负责宝马专属停车位的保安和管理人员们说,只允许车头有宝马logo的汽车进入BMW的免费停车场。
话说小王同学,她努力工作了一段时间,终于决定在自己的26岁生日时,买了一辆宝马车作为送给自己的礼物。
但是,过了一段时间,她的一个小姐妹买了一辆保时捷,她瞬间爱上了保时捷品牌,但是那个车子太贵了,自己根本买不起的,为了圆梦保时捷,她做了一个“大胆”的决定,把车头宝马的logo扣掉换上了保时捷的logo。她看到自己的车子也是保时捷了,心中那种满足感油然而生。
但是,当有一次她要出差去机场,知道飞机场有宝马的免费停车位,她高高兴兴的就开车过去了,但是保安大爷说什么都不让她开进去,虽然她的车子的确是宝马汽车,但是车子的logo却不是宝马的,成为了一辆“四不像”汽车……
一、原则定义
通过上面的例子,我们可以看到小王遇到的问题就是,自己的车标不是宝马车标,所以不符合宝马公司允许停车的标准了。那么,我们可以这样认为:
【宝马汽车】是父类,它有一个方法是
getLogo()
,这个方法会返回BMW
;【3系、X3、5系、7系……】是子类,继承了父类的
getLogo()
,调用后也会返回BMW
;
所以宝马公司就有理由决定,只要调用getLogo()方法且返回“BMW”的车子,都可以被认为是宝马汽车,那么自然而然可以通过这个条件,来限制哪些车辆可以免费停到BMW的专属车位了。而小王自己把方法getLogo()
进行了修改,使得这个方法返回了Porsche
,造成了原本是宝马车却无法停到BMW专属车位的系统异常。
了解了上面的故事,我们来真正的学习一下里式替换,它是什么含义:
LSP(
Liskov Substitution Principle
):子类对象能够替换父类对象,而程序逻辑不变。继承必须确保父类所拥有的性质在子类中仍然成立。也可以理解为:子类可以扩展父类的功能,但不能改变父类原有的功能,建议多用组合,少用继承。
那么,此处也会有同学疑惑,“多用组合,少用继承
”这个不应该是合成复用原则吗?如果我们是针对7种设计原则的维度来说,这个是没错的。如果我们针对6种设计模式来说,是不包含合成复用原则的,而“多用组合,少用继承”就被包含在了里式替换中,作为里式替换原理之后的一种编码建议。
二、原则实践
上面的故事和原理相信大家都已经有所了解了,我们还是举一个业务上的小例子,再来加深说明一下违反里式替换后可能会出现的问题。
业务背景:
【1】在一个父类
Pay
中,存在一个通用方法execute(),该方法是用于执行某项通用的业务操作;【2】然后,针对这个父类A我们继承了多个子类,分别是
AlibabaPayImpl
、WechatPayImpl
、JDPayImpl
……【3】此时呢,我们违反了LSP原则,将
JDPayImpl
类中的execute()
方法进行了自定义,但是对于父类Pay来说,它是不知道的。【4】那么,过了一段时间,公司这边说,需要在execute()方法的业务上加上某些规则校验,那么由于子类都继承了这个方法,所以开发人家就只需要在
Pay
类的execute()
方法上添加这些规则校验,所有的子类就都同时拥有了这种规则校验了。【5】但是,由于
JDPayImpl
自己实现了父类的execute()
方法,所以它没有继承规则校验功能,系统出现了漏洞……
今天的文章内容就这些了:
写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享 。
更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ \(^o^)/ ~ 「干货分享,每天更新」