里氏替换原则
跳到导航
跳到搜索
什么是里氏替换原则(定义)
Liskov Substitution Principle,LSP,它有两种定义:
第一种定义
如果每一个类型为 S 的对象 o1,都有类型为 T 的对象 o2,
使得以 T 定义的所有程序 P 在所有的对象 o1 都替换成 o2 时,程序 P 的行为没有发生变化,那么类型 S 是类型 T 的子类型。
第二种定义
所有引用基类的地方必须能透明地使用其子类的对象。
第二个定义是最清晰明确的,通俗点讲,只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是子类还是父类。
但是反过来就不行了,有子类出现的地方,父类未必就能适应。
定义的4层含义
子类必须完全实现父类的方法
示例:
/**
* 枪支的抽象类
*/
public abstract class AbstractGun {
//强用来干什么的?杀敌!
public abstract void shoot();
}
public class Handgun extends AbstractGun {
//手枪的特点是携带方便,射程短
@Override
public void shoot() {
System.out.println("手枪射击...");
}
}
public class Rifle extends AbstractGun{
//步枪的特点是射程远,威力大
@Override
public void shoot() {
System.out.println("步枪射击...");
}
}
public class MachineGun extends AbstractGun{
@Override
public void shoot() {
System.out.println("机枪扫射...");
}
}
public class Soldier {
//定义士兵的枪支
private AbstractGun gun;
//给士兵一支枪
public void setGun(AbstractGun gun) {
this.gun = gun;
}
public void killEnemy() {
System.out.println("士兵杀敌...");
gun.shoot();
}
}
public class Client {
public static void main(String[] args) {
//产生一个士兵
Soldier soldier = new Soldier();
//给士兵一支枪
soldier.setGun(new Rifle());
soldier.killEnemy();
}
}
可以通过替换传入 soldier 的 AbstractGun,灵活地在 MachineGun、Pistol、Rifle 之间切换
注意:在类中调用其他类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已经违背了 LSP 原则
如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。
参考
《设计模式之禅》