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 对象时,结果不符合预期,这就违反了里氏替换原则。

总结

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

相关推荐
阿黄学技术1 分钟前
ReentrantLock实现公平锁和非公平锁
java·开发语言·算法
探索未来 航行现在13 分钟前
Go语言--语法基础4--基本数据类型--类型转换
开发语言·后端·golang
hacker_lpy13 分钟前
python全自动爬取m3u8网页视频(各类网站都通用)
开发语言·python·m3u8视频·视频爬虫
虾球xz21 分钟前
c++环境和vscode常用的一些有用插件
c++·ide·vscode
柏木乃一25 分钟前
平衡二叉搜索树模拟实现1-------AVL树(插入,删除,查找)
c++·学习·程序人生·算法·二叉搜索树·avl树·平衡二叉搜索树
立秋678926 分钟前
3D人物关系图开发实战:Three.js实现自动旋转可视化图谱(附完整代码)
开发语言·javascript·3d
chao_78929 分钟前
PyQt5基本介绍
开发语言·qt
我命由我1234530 分钟前
C++ - 数据容器之 forward_list(创建与初始化、元素访问、容量判断、元素遍历、添加元素、删除元素)
c语言·开发语言·c++·后端·visualstudio·visual studio·后端开发
Cxzzzzzzzzzz38 分钟前
go语言实现用户管理系统
开发语言·后端·golang
智者知已应修善业44 分钟前
【51单片机6位数码管显示时间与秒表】2022-5-8
c语言·c++·经验分享·笔记·单片机·算法·51单片机