介绍
里氏替换原则(Liskov Substitution Principle,LSP)是面向对象编程中的一个重要原则,由Barbara Liskov提出,主要阐述了子类在继承父类时应该遵循的规则,即在任何父类出现的地方都可以替换为其子类,且不会导致原有程序的错误或异常行为。
原则
父类出现的地方一定可以用子类的对象替换,子类不可以改变父类的行为和状态
违反里氏替换原则的前提是错误的继承
不重写父类的默认方法,只重写父类的抽象方法。如果重写了父类的默认方法,那么就是修改了父类的行为
正反案例
反例
一个不符合LSP
的例子是一个鸟类的继承结构。如果定义一个鸟类的基类,其中有一个fly()
方法,那么这个方法可以被所有的鸟类继承并重写。但是,如果我们在这个基类中添加了一个walk()
方法,因为有些鸟类不能够行走,所以它们必须空实现这个方法。这就违反了LSP
,因为这个基类不能够被所有的子类无歧义地替换。
正例
一个符合LSP
的例子是一个基类Shape
,其中有一个getArea()
方法,这个方法可以被所有的形状类继承并重写。如果我们在Shape
基类中添加了一个getColor()
方法,因为颜色对于形状而言是没有意义的,所以它们可以空实现这个方法,这样所有的子类都可以无歧义地替换基类。
简单来说就是:
如果我们需要在所有形状中添加颜色有关的方法,但是部分子类不需要实现这个方法。可以定义为有方法体的方法(在接口中叫默认方法),这样子类不要强制实现这个方法,保证所有的子类都可以继承父类。
//父类
class Shape {
//父类的方法
public double getArea() {
return 0.0;
}
}
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
}
class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double getArea() {
return width * height;
}
}
这里,Shape
类作为一个基类,定义了一个getArea()
方法,Circle
和Rectangle
都继承了Shape
类,并且重写了getArea()
方法。这样,我们可以定义一个Shape
类型的变量,并将它赋值为一个Circle
或Rectangle
对象,因为它们都是Shape
类型的子类,并且可以无歧义地替换基类。
特点
- 里氏替换原则强调子类必须完全实现父类的(抽象)方法。如果子类不能完全实现基类的方法,那么它们应该抛出一个异常,或者定义一个默认的实现(比如接口中的默认方法)。
- LSP支持多态,可以在程序中使用父类对象代替子类对象,提高代码的可扩展性和可维护性。
注意事项(*
重点)
- LSP 原则是维护面向对象系统的正确性和稳定性的重要手段,但是在实际开发过程中,需要权衡不同原则的利弊,合理选择实现方式。
- 在使用 LSP 原则时,需要对系统进行全面的分析和设计,充分考虑各种可能的情况,以确保系统的正确性和稳定性。