里氏替换原则(Liskov Substitution Principle,LSP)是面向对象编程中的一个原则,由计算机科学家Barbara Liskov首次提出。目的是确保派生类(子类)可以替换基类(父类)并且程序仍然能够保持正确性
。
-
里氏替换原则是什么?
里氏替换原则主要是为了解决
使用继承时可能出现的一些问题,主要强调了继承关系中的子类型应该能够替代其基类型而不引起错误
。保持父类的行为:子类应该能够替换父类并且不会改变程序的正确性。
不增加异常:子类在继承父类时,不能抛出比父类更多或更严重的异常。
不降低父类方法的可访问性:子类不能降低父类方法的访问级别。如果父类方法是
public
,那么子类重写的方法也必须是public
。
class Bird {
void fly() {
System.out.println("This bird can fly.");
}
}
class Sparrow extends Bird {
// Sparrow 类继承了 fly 方法
}
public class Main {
public static void makeBirdFly(Bird bird) {
bird.fly();
}
public static void main(String[] args) {
Bird bird = new Bird();
Sparrow sparrow = new Sparrow();
makeBirdFly(bird); // 输出: This bird can fly.
makeBirdFly(sparrow); // 输出: This bird can fly.
}
}
这个子类 Sparrow
保持了基类 Bird
的行为,即它也有 fly
方法。现在,我们可以使用里氏替换原则,将 Sparrow
对象无缝地替换为 Bird
对象
刚刚的代码设计不就是简单利用了面向对象的多态特性,这个跟多态有什么区别?
虽然里氏替换原则与多态性有一定的重叠,但两者并不完全相同。里氏替换原则更关注于设计层面
,强调在使用继承时如何确保子类能够正确替换基类。多态性则是一种在运行时动态选择方法
实现的机制,是一种代码执行的行为。
能不能把例子改成只遵循多态,没有遵循里氏替换的情况?
class Bird {
void fly() {
System.out.println("This bird can fly.");
}
}
class Sparrow extends Bird {
// Sparrow 类重写了fly()
@Override
void fly() {
if(birdCanFly()){
System.out.println("This bird can fly.");
}
}
private Boolean birdCanFly(){
//判断鸟是不是有翅膀
}
}
public class Main {
public static void makeBirdFly(Bird bird) {
bird.fly();
}
public static void main(String[] args) {
Bird bird = new Bird();
Sparrow sparrow = new Sparrow();
makeBirdFly(bird); // 输出: This bird can fly.
makeBirdFly(sparrow); // 输出: This bird can fly.
}
}
虽然改造之后的代码仍然可以通过 Java 的多态语法,动态地用子类 Sparrow 来替换父类 Bird,也并不会导致程序编译或者运行报错。但是,我们在Sparrow的 fly() 方法里引入了额外的判断。 从设计思路上来讲,Sparrow 的设计是不符合里式替换原则的。
2. 违反里式替换原则会有什么问题?
- 不符合预期行为: 代码的使用者可能期望一个子类型的实例可以替换基类型的实例而不引起错误。如果违反了LSP,这种替换可能会导致意外的行为,从而破坏了代码的正确性和可靠性。
- 破坏多态性: 里式替换原则与多态性紧密相关。如果子类型不完全替代基类型,多态性将无法正常工作。这可能导致代码逻辑混乱,难以理解和维护。
- 引入错误: 在违反LSP的情况下,对基类型代码的修改可能会导致子类型的错误。这违背了继承的基本目标之一,即在不影响现有功能的情况下引入新功能。
- 不适用于通用的抽象: 如果子类型不适合替代其基类型,那么这种抽象将无法被广泛使用,限制了代码的可复用性。
3. 怎么样用好呢?
那就是将父类和子类的代码相互替换
。如果某些单元测试运行失败,就有可能说明,子类的设计实现没有完全地遵守父类的约定,子类有可能违背了里式替换原则。
多态 是面向对象编程实现
,也是面向对象编程语言的一种语法。是一种代码实现的思路。
里式替换是一种设计思想
,用来指导继承关系中子类该如何设计,子类的设计要保证在替换父类的时候,不改变原有程序的逻辑及不破坏原有程序的正确性。