前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2022-09-20 里氏替换

2022-09-20 里氏替换

作者头像
三流编程
发布2022-09-30 08:54:35
1940
发布2022-09-30 08:54:35
举报

继续阅读《设计模式之禅》第二章——里氏替换原则,英文 Liskov Substiution Principle,简称 LSP,查了下这个 Liskov,全名 Barbara Liskov,是位大佬。

含义是父类能出现的地方,子类就可以出现,不会引起任何错误或异常。当然子类出现的地方,不要求父类能出现。

直觉上子类是拓展了父类,父类能做的事确实子类都应该能做到,子类可能有自己独有的东西,父类无法实现。

定义时尽量用父类或接口,这样使用时可以传递各种不同实现的子类。

在使用父类定义的逻辑中,如果对于某个子类需要单独处理,那就不要让它成为子类。

如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。`

书中举的例子是士兵可以用各种枪支杀敌,但传过来一把玩具枪就歇菜了,逻辑里需要单独判断类是不是玩具枪,这种写法打破了原有的逻辑,一种特例判断,不好。索性玩具枪自己单独一个类,没有依赖关系。

使用特定子类的地方,不一定还能用父类,比如需要一个房子睡觉,房子有子类别墅大三居地下室隔断,给人配房子那可以配各种子类,都能用来睡觉,但如果配的就是 隔断,直接传房子就不行了,像我这种只能住10平方隔断的人,你塞来一个房子,那租金是1000还是10000,谁也保证不了,所以我明确就要住隔断就请只给我传递隔断,当然传递隔断的子类就又符合 LSP 了,比如传个暗隔明隔实隔等等。

子类输入参数范围可以扩展

比如父类的方法参数是 HashMap,子类同名方法参数是 Map,方法名相同,参数类型不同,属于重载。

这样父类调用传一个 HashMap 参数,父类方法被执行,根据 LSP 原则,父类可以被替换为子类,还是执行子类从父类继承来的方法,即执行那个参数是 HashMap 的方法。

这是希望的。如果父类参数是 Map,子类是 HashMap 相当于范围缩小,这样父类传参 HashMap,执行到参数是 Map 的方法,当父类被子类替换后,会执行到子类参数是 HashMap 的方法,而不是从父类继承的参数为 Map 的方法。

这是不希望的,因为进入了子类自己的方法,根据 LSP 有父类的地方就可以用子类,本来是父类的通用逻辑,但是这样走子类却走入了子类特有逻辑。比如

代码语言:javascript
复制
// 父类
public class Person {
    public void eat(Breakfast bf) {
        print("太好吃了")
    }
}
// 子类
public class Star extends Person {
    public void eat(Breakfast50 bf) {
        print("650的早餐都不够?50元,你喂狗的吗")
    }
}

// 我
Person programmer = new Person();
// 苏老师
Star star = new Star();
// 50元的豪华早餐
Breakfast50 bf50 = new Breakfast50();
// 输出“太好吃了”,领导很开心,工人们还是很容易满足的嘛
worker.eat(bf50);
// 输出“650的早餐都不够?50元,你喂狗的吗“,领导脸黑了,我正在弘扬节约精神呢,你这狗东西敢打我脸
star.eat(bf50);

上面的打脸原因在于子类缩小了参数范围,在可以使用父类的地方改用子类就执行了子类特有的内容,引起了不适。如果反过来

代码语言:javascript
复制
public class Person {
    public void eat(Breakfast50 bf) {
        print("一顿早餐就50,也太豪华了吧")
    }
}
// 子类
public class Star extends Person {
    public void eat(Breakfast bf) {
        print("你这早餐多少钱,没1000就赶紧给我滚开")
    }
}
Person programmer = new Person();
Star star = new Star();
Breakfast50 bf50 = new Breakfast50();
programmer.eat(bf50); // 输出"一顿早餐就50,也太豪华了吧"
star(bf50); // 输出"一顿早餐就50,也太豪华了吧"

无论什么人,吃 50 元早餐都很满足,你看贫富分化也看不出来了,社会也和谐了,领导也开心了。

子类返回值范围可以被缩小

重写时返回值必须是父类返回值的子类。重载时按照上面一条规则,如果参数是父类定义的参数类型,不会执行到子类,子类返回值也无所谓。

书里说最佳实践是尽量减少子类个性,但子类不就是要拥有各自的个性吗?把 Star 当普通 Person 去 eat 普通的 Breakfast,确实委屈了 Star,但为了隐藏贫富差距的矛盾,就得这么用,除非 Star 别继承 Person。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-09-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档