Java七大设计原则(3/7)——里氏替换原则


定义

里氏代换原则,任何基类可以使用的地方,子类一定可以使用。但反过来不一定成立,子类出现的地方,基类不一定能使用。即子类可以拓展父类,但不能更改父类原有的功能。


概述

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

后两句可以概括为:接受的参数(类型)范围比父类大,返回的参数(类型)范围比父类小


作用

  • 里氏替换原则是实现开闭原则的重要方式之一;
  • 解决了继承中重写父类造成的可复用性变差的问题;
  • 是动作正确性的保证,即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性;
  • 加强程序的健壮性,同时变更时可以做到非常好的兼容性,提高程序的维护性、可扩展性,降低需求变更时引入的风险。

代码实现

假设有一个燕子和一个企鹅,它们都是鸟,但是企鹅不会飞。

现在要计算它们移动一定距离所需的时间。

不使用里氏替换原则

定义Bird类

/**
 * @author John117
 * @date 2022/05/22  12:09
 */
public class Bird {
    double flySpeed;

    public void setFlySpeed(double speed) {
        this.flySpeed = speed;
    }

    public double getFlyTime(double distance){`在这里插入代码片`
        return distance/flySpeed;
    }
}

定义Swallow类

/**
 * @author John117
 * @date 2022/05/22  12:11
 */
public class Swallow extends Bird{

}

定义Penguin类

/**
 * @author John117
 * @date 2022/05/22  12:12
 */
public class Penguin extends Bird {
    @Override
    public void setFlySpeed(double speed) {
        this.flySpeed = 0;
    }
}

测试类

/**
 * @author John117
 * @date 2022/05/22  12:13
 */
public class Test {
    public static void main(String[] args) {
        Bird swallow = new Swallow();
        Bird penguin = new Penguin();
        swallow.setFlySpeed(180);
        penguin.setFlySpeed(180);
        System.out.println("飞行距离为300公里");
        System.out.println("swallow将飞行 "+swallow.getFlyTime(300)+" 小时");
        System.out.println("penguin将飞行 "+penguin.getFlyTime(300)+" 小时");
    }
}

结果为在这里插入图片描述
很明显,这不是想要的结果,因为企鹅不会飞,它的飞行速度为0,所以结果才会显示 Infinity。

再来看看使用里氏替换原则是怎样的


使用里氏替换原则

这里把Bird类和Penguin类进一步抽象,定义一个Animal类。

Animal类有一个 runSpeed 属性,这很合理,毕竟鸟也能 run 嘛。

/**
 * @author John117
 * @date 2022/05/22  12:17
 */
public class Animal {
    double runSpeed;

    public void setRunSpeed(double runSpeed) {
        this.runSpeed = runSpeed;
    }

    public double getRunTime(double distance) {
        return distance/runSpeed;
    }
}

再定义Brid类,Bird类继承Animal类,Bird类有自己的 flySpeed 属性

/**
 * @author John117
 * @date 2022/05/22  12:18
 */
public class Bird extends Animal{
    double flySpeed;

    public void setFlySpeed(double flySpeed) {
        this.flySpeed = flySpeed;
    }

    public double getFlyTime(double distance) {
        return distance/flySpeed;
    }
}

再定义Swallow类与Penguin类,分别继承Bird类和Animal类。

这里Swallow就有 runSpeed 属性和 flySpeed 属性了,而因为Penguin不能飞,所以它只有 runSpeed 属性

/**
 * @author John117
 * @date 2022/05/22  12:20
 */
public class Swallow extends Bird{
}
/**
 * @author John117
 * @date 2022/05/22  12:20
 */
public class Penguin extends Animal{
}

测试

/**
 * @author John117
 * @date 2022/05/22  12:21
 */
public class Test {
    public static void main(String[] args) {
        Bird swallow = new Swallow();
        Animal penguin = new Penguin();
        swallow.setFlySpeed(120);
        penguin.setRunSpeed(20);
        System.out.println("如果移动300公里:");
        System.out.println("swallow将移动:" + swallow.getFlyTime(300) + "小时");
        System.out.println("penguin将移动:" + penguin.getRunTime(300) + "小时");
    }
}

输出结果
在这里插入图片描述
这里才是符合预期的地方。

这个例子展示了里氏替换原则作用之一——保证动作正确性,其实里氏替换原则还能做更多事情。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值