1里氏替换原则介绍
在面向对象思想中:继承是很重要的思想
继承的优势:
- 提高代码的复用性(每个子类有拥有父类的属性和方法)
- 提高代码的可扩展性
劣势
- 继承是侵入性的(只要继承,就必须拥有父类的属性和方法)
- 继承机制很大的增加了耦合性,降低了代码的灵活性
- 父类的常量、变量和方法被修改时,需要考虑子类的修改
在编程中正确的使用继承思想:就需要使用里氏替换原则
来源:里氏替换原则(Liskov Substitution Principle,LSP)是1988年,麻省理工学院一位姓里的女士提出的
定义:①如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型S是类型T的子类型。
(If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T,the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.)
②所有引用基类的地方必须能透明地使用其子类的对象。
(Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.)
通俗讲:只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。
白话讲:子类可以扩展父类的功能,但是子类不能修改父类原有的功能,
里氏替换原则规范了继承规则
2模拟场景(违背原则)
随着市场发展有了电子香烟,早期时还没有电子烟
/**
* @author nzy
* @create 2021-12-09 17:42
* 香烟基类
*/
public class Cigarette {
public void lightCigarette(String name){//用打火机点烟
System.out.println("点烟的名称是"+name);
}
public void smokeing(String name){
lightCigarette(name);
System.out.println("抽烟的名称是"+name);
}
}
public class YuXiCigarette extends Cigarette{//中华
}
public class ZhongHuaCigarette extends Cigarette{//玉溪
}
/**
* @author nzy
* @create 2021-12-09 17:47
* 电子烟
*/
public class ElectronicCigarette extends Cigarette{
}
//调用Client类
public class PersonClient {//人抽烟
public static void main(String[] args) {
Cigarette cigarette = new YuXiCigarette();
cigarette.smokeing("玉溪");
Cigarette electronicCigarette = new ElectronicCigarette();//调用测试人员并不知道电子烟 是不需要点火的,所以就gg了
electronicCigarette.smokeing("电子烟");
}
}
在没有电子烟的时候都是这个过程,现在有电子烟也点烟,所以是有问题的,
3模拟场景(符合原则)
思路:点火是子类特有的方法,所要不要在父类中定义
package com.design.philosophy.lsp.right;
/**
* @author nzy
* @create 2021-12-09 17:42
* 香烟类 创建香烟基类 去掉点火方法,让子类自己去实现
*/
public class Cigarette {
public void smokeing(String name){
System.out.println("抽烟的名称是"+name);
}
}
package com.design.philosophy.lsp.right;
/**
* @author nzy
* @create 2021-12-09 18:11
* 传统香烟需要点火
*/
public class TraditionCigarette extends Cigarette{
public void lightCigarette(String name){//用打火机点烟
System.out.println("点烟的名称是"+name);
}
}
package com.design.philosophy.lsp.right;
/**
* @author nzy
* @create 2021-12-09 17:47
* 电子烟
*/
public class ElectronicCigarette extends Cigarette {
//不需要写点火方法
public void userElectricity() {
System.out.println("抽电子烟用电中");
}
}
package com.design.philosophy.lsp.right;
/**
* @author nzy
* @create 2021-12-09 17:53
*/
public class PersonClient {//人抽烟
public static void main(String[] args) {
TraditionCigarette traditionCigarette = new TraditionCigarette();//传统香烟
traditionCigarette.lightCigarette("中华");
traditionCigarette.smokeing("中华");
ElectronicCigarette electronicCigarette = new ElectronicCigarette();
electronicCigarette.smokeing("电子烟");
}
}
4总结
代码层面来讲:
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
- 子类可以增加自己特有的方法
- 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松。
- 当子类的方法实现父类的方法(重写、重载或实现抽象方法)时,方法的后置条件(即方法的输出或返回值)要比父类的方法更严格或与父类的方法相等
在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖 来解决问题。
里氏替换原则为继承提供了规范
使用里氏替换原则的目的就是增强程序的健壮性,版本升级时也可以保持非常好的兼容性。即使增加子类,原有的子类还可以继续运行。在实际项目中,每个子类对应不同的业务含义,使用父类作为参数,传递不同的子类完成不同的业务逻辑,