深入浅出设计模式原则之里氏代换原则(Liskov Substitution Principle)

下面以“几维鸟不是鸟”为例来说明里氏替换原则(程序源码)。

#include <QCoreApplication>
#include <iostream>

/*!
 * \brief 鸟类
 */
class Bird{
public:
    double _fly_speed;

    void SetSpeed(double speed){
        _fly_speed = speed;
    }

    double GetFlyTime(double distance){
        return (distance/_fly_speed);
    }
};

/*!
 * \brief 燕子类
 */
class Swallow:public Bird{

};

/*!
 * \brief 几维鸟类
 */
class BrownKiwi:public Bird{
public:
    void SetSpeed(double speed){
        _fly_speed = 0;
    }
};


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Swallow *bird1 = new Swallow;
    bird1->SetSpeed(120);
    std::cout<<bird1->_fly_speed<<std::endl;
    std::cout<<bird1->GetFlyTime(300)<<std::endl;

    BrownKiwi *bird2 = new BrownKiwi;
    bird2->SetSpeed(120);
    std::cout<<bird2->_fly_speed<<std::endl;
    std::cout<<bird2->GetFlyTime(300)<<std::endl;
    return a.exec();
}

运行结果:

120
2.5
0
inf

 

分析:鸟一般都会飞行,如燕子的飞行速度大概是每小时 120 千米。但是新西兰的几维鸟由于翅膀退化无法飞行。假如要设计一个实例,计算这两种鸟飞行 300 千米要花费的时间。显然,拿燕子来测试这段代码,结果正确,能计算出所需要的时间;但拿几维鸟来测试,结果会发生“除零异常”或是“无穷大”,明显不符合预期,其类图如图 1 所示。
 

“几维鸟不是鸟”实例的类图
图1 “几维鸟不是鸟”实例的类图

 程序运行错误的原因是:几维鸟类重写了鸟类的 setSpeed(double speed) 方法,这违背了里氏替换原则。

解决方法:

取消几维鸟原来的继承关系,定义鸟和几维鸟的更一般的父类,如动物类,它们都有奔跑的能力。几维鸟的飞行速度虽然为 0,但奔跑速度不为 0,可以计算出其奔跑 300 千米所要花费的时间。其类图如图 2 所示。

“几维鸟是动物”实例的类图

#include <QCoreApplication>
#include <iostream>

/*!
 * \brief 动物类
 */
class Animal{
public:
    double _run_speed;

    void SetRunSpeed(double speed){
        _run_speed = speed;
    }

    double GetRunTime(double distance){
        return (distance/_run_speed);
    }
};

/*!
 * \brief 鸟类
 */
class Bird:public Animal{
public:
    double _fly_speed;

    void SetSpeed(double speed){
        _fly_speed = speed;
    }

    double GetFlyTime(double distance){
        return (distance/_fly_speed);
    }
};

/*!
 * \brief 燕子类
 */
class Swallow:public Bird{

};

/*!
 * \brief 几维鸟类
 */
class BrownKiwi:public Animal{

};


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Swallow *bird1 = new Swallow;
    bird1->SetSpeed(120);
    std::cout<<bird1->_fly_speed<<std::endl;
    std::cout<<bird1->GetFlyTime(300)<<std::endl;

    BrownKiwi *bird2 = new BrownKiwi;
    bird2->SetRunSpeed(120);
    std::cout<<bird2->_run_speed<<std::endl;
    std::cout<<bird2->GetRunTime(300)<<std::endl;
    return a.exec();
}

 运行结果:

120
2.5
120
2.5

 

里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

根据上述理解,对里氏替换原则的定义可以总结如下:

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
  • 子类中可以增加自己特有的方法
  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松
  • 当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的的输出/返回值)要比父类的方法更严格或相等

参考

  1. 设计模式六大原则(2):里氏替换原则
  2. 里氏替换原则——面向对象设计原则

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值