继续阅读《设计模式之禅》第二章——里氏替换原则
,英文 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。