• 设计原则四:里氏替换原则


    派生类的对象能够替换其基类的对象被使用

    定义

    里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理。里氏替换原是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范

    举个例子,比如有一个基类Animal和一个派生类Cat,程序中任何使用Animal对象的地方,我们都可以放Cat对象上去(派生类的对象能够替换子类的对象),而完全不扰乱程序的逻辑(程序里关于Animal的假设,Cat都不能打破)。

    更通俗一点描述就是,假如我们给Animal设定方法,可以设定一个吃(), 这个没问题,凡是动物都会吃东西,猫也会吃东西。但是如果给动物设定方法:走路(),就不太好了,因为不是所有的动物都会走路,比如鱼。假如Animal类存在走路这个方法,且Animal对象调用了该方法,此时再用Cat对象替换Animal对象就不符合常识了,此时违背了里氏替换原则。

    实现方法

    里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法(除非基类中的纯虚函数必须要重写,虚函数可以重写,普通函数不能或者尽量不要重写)

    • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法

    • 子类中可以增加自己特有的方法

    • 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松

    • 当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的的输出/返回值)要比父类的方法更严格或相等

    案例分析

    [示例1] 几维鸟不是鸟–破坏原则案例

    分析:鸟一般都会飞行,如燕子的飞行速度大概是每小时 120 千米。但是新西兰的几维鸟由于翅膀退化无法飞行。假如类的设计如下:基类为鸟(具有飞行速度这一属性且v大于0),燕子和几维鸟继承自鸟(实际上不是所有鸟的v都大于零,因此几维鸟对象替换基类对象时可能会出错)。

    #include 
    class Bird {
    public:
        float flySpeed; //不符合里氏替换原则
        void setSpeed(float v) {
            flySpeed = v;
        }
        float getFlyTime(float dis) {
            return dis/flySpeed;
        }
    };
    class Swallow : public Bird {
    
    };
    class BrownKiwi : public Bird {
    
    };
    int main(){
        Bird* b1 = new Swallow; // &b1 是
        Bird* b2 = new BrownKiwi;
    
        b1->setSpeed(300);
        b2->setSpeed(0);
    
        std::cout<<"b1 fly time is: " << b1->getFlyTime(300)<<std::endl;
        std::cout<<"b2 fly time is: " << b2->getFlyTime(300)<<std::endl;
    
        return 1;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    [示例2]

    正确的做法是:取消几维鸟原来的继承关系,定义鸟和几维鸟的更一般的父类,如动物类,它们都有呼吸的能力,鸟类继承自动物类,燕子继承自鸟类;几维鸟继承自动物类。

    #include 
    
    class Animal{
    public:
        float runSpeed;
        virtual void setRunSpeed(float v) {
            runSpeed = v;
        }
        virtual float getRunTime(float dis) {
            return dis/runSpeed;
        }
    };
    
    class Bird : public Animal{
    public:
        float flySpeed;
        virtual void setSpeed(float v) {
            flySpeed = v;
        }
        virtual float getFlyTime(float dis) {
            return dis/flySpeed;
        }
    };
    
    class Swallow : public Bird {
    
    };
    
    class BrownKiwi : public Animal {
    
    };
    
    int main(){
        Bird* b1 = new Swallow; // &b1 是
        Animal* b2 = new BrownKiwi;
    
        b1->setSpeed(300);
        b1->setRunSpeed(0.01);
        b2->setRunSpeed(0.2);
    
        std::cout<<"b1 fly time is: " << b1->getFlyTime(300)<<std::endl;
        std::cout<<"b1 run time is: " << b1->getRunTime(300)<<std::endl;
        std::cout<<"b2 fly time is: " << b2->getRunTime(300)<<std::endl;
    
        return 1;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
  • 相关阅读:
    Flutter:open_file打开本地文件报错问题
    【Kubernetes三大核心概念】
    神经网络在控制中的作用,神经网络控制基本原理
    nginx隐藏版本号和标识
    DRM系列(4)之drmModePageFlip
    Leetcode 4.21
    可视化大屏报表的设计与制作 | 附成果图
    照片会说话的软件,快来试试这个小功能
    XML解析入门
    mybatis缓存介绍
  • 原文地址:https://blog.csdn.net/u011852872/article/details/126048726