《设计模式》——里氏替换原则

1、什么是里氏替换原则

Liskov于1987年提出了一个关于继承的原则“Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.”——“继承必须确保父类所拥有的性质在子类中仍然成立。”也就是说,当一个子类的实例应该能够替换任何其父类的实例时,它们之间才具有is-A关系。该原则称为Liskov Substitution Principle——里氏替换原则。

2、代码示例
正方形不是长方形:

在我们的认知范围内,长方形的长不等于宽,正方形是长等于宽的长方形,正方形是一种特殊的长方形。但在实际编码过程中遇到的情况是怎么样的呢,通过代码分析:

package cn.com.design.model.test.liskvorole.negative;

/**
 * 项目名称:OpenCloseRole
 * 类 名 称:Test
 * 类 描 述:TODO
 * 创建时间:2021/1/18 下午8:32
 * 创 建 人:wteng
 */

/**
 * 在我们的认知范围内,一致认为正方形就是一个特殊的长方形
 * 我们也知道,两个类只要有"is a"关系,两个类就可以发生继承关系。
 * 里氏替换原则:任何使用父类的地方,都能被透明的替换成子类。替换成子类后,程序行为不会发生问题。
 * 在里氏替换原则的指导方针下,可得出:仅仅依据两个类之间有没有"is a"的关系,来判断两个类能不能发生继承关系,是不够的,
 *
 * 以下是反例,正方形不能替换长方形,替换后测试失败
 */

class Rect {
    private int width;
    private int height;

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int area() {
        return getHeight()*getWidth();
    }
}

class Squaer extends Rect{
    private int sideLength;

    @Override
    public void setWidth(int width) {
        this.sideLength = width;
    }

    @Override
    public void setHeight(int height) {
        this.sideLength = height;
    }

    @Override
    public int getWidth() {
        return sideLength;
    }

    @Override
    public int getHeight() {
        return sideLength;
    }
}
class Foo {
    public void testArea(Rect rect) {
        rect.setWidth(10);
        rect.setHeight(15);

        if ((rect.area() == 150)) {
            System.out.println("测试通过");
        } else {
            System.out.println("测试失败");
        }
    }
}

public class Test {
    public static void main(String[] args) {
        Foo foo = new Foo();
//        Rect rect = new Rect();
        Rect squer = new Squaer();
        foo.testArea(squer);
    }

}

但是运行结果为测试失败,说明正方形是特殊的长方形违背了里氏替换原则,为了解决上边问题,仅仅断绝square和Rect的继承关系即可
除此之外,类似经典的案例还有鸵鸟飞鸟。

3.总结

所谓对象是一组状态和一系列行为的组合。状态是对象的内在特性,行为是对象的外在特性。LSP所表述的就是在同一个继承体系中的对象应该有共同的行为特征。我们在设计对象时是按照行为进行分类的,只有行为一致的对象才能抽象出一个类来。设置长方形的长度的时候,它的宽度保持不变,设置宽度的时候,长度保持不变。正方形的行为:设置正方形的长度的时候,宽度随之改变;设置宽度的时候,长度随之改变。所以,如果我们把这种行为加到基类长方形的时候,就导致了正方形无法继承这种行为。我们“强行”把正方形从长方形继承过来,就造成无法达到预期的结果。鸵鸟非鸟,能飞是鸟的特性,但鸵鸟是不能飞的,我们强行将其归为鸟类,最终导致代码出错。

所有子类的行为功能必须和其父类持一致,如果子类达不到这一点,那么必然违反里氏替换原则。在实际的开发过程中,不正确的派生关系是非常有害的。伴随着软件开发规模的扩大,参与的开发人员也越来越多,每个人都在使用别人提供的组件,也会为别人提供组件。最终,所有人的开发的组件经过层层包装和不断组合,被集成为一个完整的系统。每个开发人员在使用别人的组件时,只需知道组件的对外裸露的接口,那就是它全部行为的集合,至于内部到底是怎么实现的,无法知道,也无须知道。所以,对于使用者而言,它只能通过接口实现自己的预期,如果组件接口提供的行为与使用者的预期不符,错误便产生了。里氏替换原则就是在设计时避免出现派生类与基类不一致的行为。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值