(LSP) 里氏替换原则 Liskov Substitution Principle
核心原则
子类必须能够替换它们的基类 (IS-A)。

场景描述
我们都知道在数学中正方形是矩形的一种特例。
我们很容易想到可以使用继承关系,即正方形作为子类继承父类矩阵。
#include <iostream>
// 矩形基类
class Rectangle {
private:
double width = 0;
double height = 0;
public:
virtual ~Rectangle() = default;
Rectangle(double w, double h) {
width = w;
height = h;
}
void set_width(double w) {
width = w;
}
void set_height(double h) {
height = h;
}
double get_area() const {
return width * height;
};
};
// 正方形继承矩形
class Square : public Rectangle {
public:
Square(double len) : Rectangle(len, len) {
}
};
void change_oneside(Rectangle& rect, double len) {
rect.set_width(len);
}
int main() {
Square squ(10);
std::cout << "Square area = " << squ.get_area() << '\n';
// 希望得到的area是400,但是最终是200
change_oneside(squ, 20);
std::cout << "After set length Square area = " << squ.get_area() << '\n';
}
问题暴露
很明显,我们知道矩形可以分别修改长和宽,而正方形所有边都一样。
如果朴素的使用直接继承的设计方案,极容易出现正方形只修改一组平行边,从而退化为矩形的情况。
这就是里氏替换原则的一个经典例子,那如何解决呢?
#include <iostream>
// 统一图形的接口
// 空的多态函数,让具体类型实现具体操作
class IGeometry {
public:
virtual void set_sideLegth(double len) {
}
virtual void set_sideLegth(double len1, double len2) {
}
virtual double get_area() const {
return 0;
};
};
// 矩形
class Rectangle : public IGeometry {
private:
double width = 0;
double height = 0;
public:
virtual ~Rectangle() = default;
Rectangle(double w, double h) {
width = w;
height = h;
}
virtual void set_sideLegth(double w, double h) override {
width = w;
height = h;
}
virtual double get_area() const override {
return width * height;
};
};
// 正方形
class Square : public IGeometry {
private:
double side = 0;
public:
Square(double len) {
side = len;
}
virtual void set_sideLegth(double len) override {
side = len;
}
virtual double get_area() const override {
return side * side;
};
};
void change_oneside(IGeometry& rect, double len) {
rect.set_sideLegth(len);
}
int main() {
Square squ(10);
std::cout << "Square area = " << squ.get_area() << '\n';
// 希望得到的area是400,但是最终是200
change_oneside(squ, 20);
std::cout << "After set length Square area = " << squ.get_area() << '\n';
}