软件设计原则之LSP里氏替换原则

(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';
}
相关推荐
咖啡八杯6 天前
设计模式中的原则
设计模式·接口隔离原则·里氏替换原则·开闭原则·合成复用原则·单一职责原则
国服第二切图仔14 天前
13其他工具 - Skill/LSP/Sleep等
linux·运维·里氏替换原则
-银雾鸢尾-16 天前
里氏替换原则
开发语言·c#·里氏替换原则
hereitis贝壳2 个月前
GC.lsp:AutoCAD 中实用的轻量化公差标注插件
jvm·里氏替换原则
crack_comet2 个月前
修复 Claude Code TypeScript LSP 在 Windows 上启动失败的问题
windows·typescript·里氏替换原则
bush42 个月前
vscode编辑设备树插件 devicetree LSP
ide·vscode·里氏替换原则
Soari2 个月前
oh-my-pi:一款面向终端的 AI Coding Agent,集成 LSP、Debugger、Subagents 与多模型路由
人工智能·里氏替换原则
Thomas21433 个月前
opencode lsp
里氏替换原则
数据知道4 个月前
claw-code 源码分析:从 REPL 到服务端——CLI / HTTP(SSE) / LSP 多入口如何共享同一颗 runtime 心?
python·网络协议·http·ai·里氏替换原则·claude code