里氏替换原则,全称Liskov Substitution Principle,英文缩写LSP。
一个程序中如果使用的是一个父类,那么该程序一定适用于其子类,而且程序察觉不出父类和子类对象的区别。也就是说在程序中,把父类替换成它的子类,程序的行为没有任何变化。
关于里氏替换原则的反例有不少,举个例子,企鹅不会飞。
Birds鸟类、Sparrow麻雀类,所有的鸟类都有飞行速度。
public abstract class Birds { //所有鸟类都应该具有飞行速度 public abstract double FlySpeed(); } public class Sparrow : Birds { public override double FlySpeed() { return 12; } }
此时添加一个Penguin企鹅类,因为企鹅也是鸟类,所以继承Birds鸟类。
public class Penguin : Birds { public override double FlySpeed() { return 0; } }
但因为企鹅不会飞,所以飞行速度设为0,这也算是实现了FlySpeed的方法,运行结果也没问题。但如果现在有一个方法Fly用来计算一只鸟飞行300米所需要的时间。
public static double Fly(Birds bird) { return 300 / bird.FlySpeed(); }
当把企鹅Penguin放进去,会出现报错,因为300/0是不合理的,这就不满足里氏替换原则。不满足该原则的原因还是因为Penguin企鹅类没有完全继承Birds鸟类,因为企鹅不能飞,实现不了FlySpeed方法。所以要解决这个问题有两种方案。
第一种是直接在Fly方法中进行判断。
public static double Fly(Birds bird) { //如果传入的类型为企鹅,默认返回0 if (bird.GetType().Equals(typeof(Penguin))) { return 0; } else { return 300 / bird.FlySpeed(); } }
可这样就会违反开闭原则,以后如果需要添加新功能,得不断修改。
第二种就是Penguin企鹅类不要继承Birds鸟类,实际上,这里Penguin企鹅就是强行继承Birds鸟类,虽然现实中企鹅也是鸟,但在代码中却不行。
总结一下里氏替换原则的特点,就是子类可以拓展父类的功能,但是不能改变父类原有的功能。子类可以重写父类的抽象方法,但不能覆盖父类的非抽象方法。子类可以增加自己独有的方法。