六、里氏替换原则
里氏替换原则(Liskov Substitution Principle,LSP)
里氏替换原则是我认为其他几个设计原则中比较难以掌握一种,首先他的定义十分晦涩。
假如存在,一个类型T
,和已经被实例的对象O
还存在,另一个类型T2
,和已经被实例的对象O2
那么存在以下情况,将所有类型为T
的对象O
都替换成类型T2
的对象O2
,程序的行为没发生什么改变。
看到这一句话给我的第一个感觉是,这讲的一定是父子关系,这是第一点。
第二点,如果说即便替换了对象,方法的功能或者说行为没有改变,也就意味着子类只是对原有父类的功能进行了他独有的增强,本质还是做了相同事,例如父类做了跳高的行为,而子类只是穿上了弹簧鞋子,跳的更高了。本质上还是跳高,所以你替换成什么,都不会影响他原有的逻辑。
所以我们总结一下就是:子类可以扩展父类的功能,但是不能改变父类原有的功能
。。
以下是我从找到的资料粘贴下来的说明,我觉得讲得很好。
1、子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
2、子类中可以增加自己特有的方法。
3、当子类的方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松。
4、当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类更严格或相等。
之前讲开闭原则
的时候留了一个坑,就是获取价格的时候。
public class TomatoEggDiscountFood extends TomatoEggFood {
public TomatoEggDiscountFood(Integer id, String name, Double price) {
super(id, name, price);
}
public Double getOriginPrice(){//这里其实违背了里氏替换法则,后会讲解,这里留个坑
return super.getPrice();
}
public Double getPrice(){
return super.getPrice() * 0.8;//修改的行为
}
}
这里其实存在问题,虽然说我们为了获得打折后的炒蛋的价格,而去重写了父类的方法,但是我们可以发现,这里其实不适用于里氏替换法则
,因为当我们将TomatoEggDiscountFood
替换成TomatoEggFood
的时候,getPrice
的行为就变得奇怪了,因为钱涨了
?虽然这都是属于付钱,但是你可以决定让我支付宝
或者微信
付款,但是你多收我钱是几个意思。所以这里付钱的行为你可以增强,但是具体付钱的金额,导致违背了里氏替换
。
解决这个方法很简单。我们别重写,额外定义我们自己独有的方法不就完了吗。
public class TomatoEggDiscountFood extends TomatoEggFood {
public TomatoEggDiscountFood(Integer id, String name, Double price) {
super(id, name, price);
}
public Double getDisCountinPrice(){//想获得打折,调用这个方法就可以了。
return super.getPrice()*0.8;
}
public Double getPrice(){
return super.getPrice();
}
}
想替换的时候也不要紧,因为你的父类不可能点出getDisCountinPrice
方法,所以也不需要担心会被替换的问题。
所以里氏替换的优点,我们也可以总结一下。
1、约束继承泛滥,开闭原则的一种体现。
2、加强程序的健壮性,同时变更时也可以做到非常好的兼容性,提高程序的维护性、扩展性。降低需求变更时引入的风险。
版权声明:本文为CSDN博主「PopCandier」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/CandyCCCation/article/details/88904701