C++设计模式:面向对象的八大设计原则之四

里氏替换原则(Liskov Substitution Principle,LSP)是面向对象设计中的一个重要原则,它指出子类必须能够替换它的基类,并且程序的行为不会发生改变。也就是说,在任何使用基类对象的地方,都可以透明地使用其子类对象。继承不仅是代码复用的手段,更重要的是表达了类型的抽象,子类是对基类的一种特殊化。

下面通过几个 C++ 代码示例来详细讲解里氏替换原则。

示例一:简单的继承与替换

首先,我们定义一个基类 Shape 表示形状,然后派生出子类 Rectangle(矩形)和 Square(正方形)。

cpp 复制代码
#include <iostream>



// 基类:形状

class Shape {

public:

    virtual double area() const = 0;

    virtual ~Shape() {}

};



// 子类:矩形

class Rectangle : public Shape {

private:

    double width;

    double height;



public:

    Rectangle(double w, double h) : width(w), height(h) {}

    double area() const override {

        return width * height;

    }

};



// 子类:正方形

class Square : public Shape {

private:

    double side;



public:

    Square(double s) : side(s) {}

    double area() const override {

        return side * side;

    }

};



// 计算形状面积的函数

void printArea(const Shape& shape) {

    std::cout << "Area: " << shape.area() << std::endl;

}



int main() {

    Rectangle rect(3, 4);

    Square square(5);



    // 可以用子类对象替换基类对象

    printArea(rect);

    printArea(square);



    return 0;

}    

代码解释

Shape 是一个抽象基类,定义了纯虚函数 area(),用于计算形状的面积。

Rectangle 和 Square 是 Shape 的子类,分别实现了 area() 函数。

printArea 函数接受一个 Shape 类型的引用作为参数,在 main 函数中,我们可以将 Rectangle 和 Square 对象传递给 printArea 函数,这体现了子类可以替换基类的特性。

示例二:违反里氏替换原则的情况

有时候,如果子类的行为与基类的预期行为不一致,就会违反里氏替换原则。例如,我们对上面的 Rectangle 和 Square 类进行修改,添加 setWidth 和 setHeight 方法。

cpp 复制代码
#include <iostream>



// 基类:矩形

class Rectangle {

protected:

    double width;

    double height;



public:

    Rectangle(double w, double h) : width(w), height(h) {}

    virtual void setWidth(double w) { width = w; }

    virtual void setHeight(double h) { height = h; }

    double area() const {

        return width * height;

    }

};



// 子类:正方形

class Square : public Rectangle {

public:

    Square(double s) : Rectangle(s, s) {}

    void setWidth(double w) override {

        width = w;

        height = w;

    }

    void setHeight(double h) override {

        width = h;

        height = h;

    }

};



// 调整矩形尺寸并打印面积的函数

void resizeAndPrint(Rectangle& rect) {

    rect.setWidth(5);

    rect.setHeight(10);

    std::cout << "Area: " << rect.area() << std::endl;

}



int main() {

    Rectangle rect(3, 4);

    Square square(5);



    // 对于矩形,预期结果是 5 * 10 = 50

    resizeAndPrint(rect);



    // 对于正方形,结果不符合预期,因为正方形的宽和高总是相等的

    resizeAndPrint(square);



    return 0;

}

代码解释

在这个例子中,Square 类继承自 Rectangle 类,但是 Square 类的 setWidth 和 setHeight 方法会同时修改宽和高,这与 Rectangle 类的预期行为不一致。

resizeAndPrint 函数期望传入的 Rectangle 对象可以独立地设置宽和高,但是当传入 Square 对象时,结果不符合预期,这就违反了里氏替换原则。

总结

里氏替换原则强调了继承关系中类型的兼容性和行为的一致性。在设计类和继承体系时,要确保子类能够完全替换基类,并且不会破坏程序的正确性。如果子类的行为与基类的预期行为不一致,就可能需要重新考虑类的设计,例如使用组合而不是继承。

相关推荐
Hellyc4 分钟前
基于模板设计模式开发优惠券推送功能以及对过期优惠卷进行定时清理
java·数据库·设计模式·rocketmq
追烽少年x12 分钟前
设计模式---观察者模式(发布-订阅模式)
网络·设计模式
秋田君12 分钟前
深入理解JavaScript设计模式之命令模式
javascript·设计模式·命令模式
花好月圆春祺夏安13 分钟前
基于odoo17的设计模式详解---享元模式
设计模式·享元模式
百锦再1 小时前
详细解析 .NET 依赖注入的三种生命周期模式
java·开发语言·.net·di·注入·模式·依赖
风吹落叶花飘荡2 小时前
2025 Next.js项目提前编译并在服务器
服务器·开发语言·javascript
失败又激情的man2 小时前
python之requests库解析
开发语言·爬虫·python
专注VB编程开发20年3 小时前
常见 HTTP 方法的成功状态码200,204,202,201
开发语言·网络协议·tcp/ip·http
有没有没有重复的名字3 小时前
线程安全的单例模式与读者写者问题
java·开发语言·单例模式
花好月圆春祺夏安3 小时前
基于odoo17的设计模式详解---命令模式
设计模式·命令模式