02.里氏替换原则 (LSP)

LSP全称

LSP , Liskov Substitution Principle , 里氏替换原则

定义

所有引用基类(父类)的地方必须能透明地使用其子类的对象。通俗讲:子类可以扩展父类的功能,但不能改变父类原有的功能

优点

  1. 是实现开闭原则的重要方式之一
  2. 克服了继承中重写父类造成的可用性变差的缺点
  3. 它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性

缺点

  1. 继承是侵入性的,只要继承就必须拥有父类的所有属性和方法
  2. 可能造成子类代码冗余,灵活性降低,因为必须拥有父类的属性和方法

实现

  • 问题由来: 有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。
  • 解决方案:
    当使用继承时,遵循里氏替换原则。类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。

里氏替换原则的核心原理是抽象,抽象又依赖于继承这个特性

  1. 子类必须完全实现父类的方法
  2. 子类可以有自己的修改(重写,重载,添加父类中没有的方法)
  3. 子类中override的方法,传入参数类型必须是与父类相同类型,或是子类型
  4. 子类中override的方法,返回类型必须是与父类相同类型,或是子类型

实例

举例说明两个数相减,由A类负责

class A {
    public int func1(int a, int b) {
        return a-b;
    }
}

public class LSPClient {
    public static void main(String[] args) {
        A a = new A();
        System.out.println("5-3=" + a.func1(5, 3));
        System.out.println("100-10=" + a.func1(100, 10));
    }
}

输出结果:
5 - 3 = 2
100 - 10 = 90

此时需要新加一个功能:两数相加,然后再与100求和,由B类负责

class A {
    public int func1(int a, int b) {
        return a - b;
    }
}

class B extends A {
    @Override
    public int func1(int a, int b) {
        return a + b;
    }

    public int func2(int a, int b) {
        return func1(a, b) + 100;
    }
}

public class LSPClient {
    public static void main(String[] args) {
        B b = new B();
        System.out.println("5-3=" + b.func1(5, 3));
        System.out.println("100-10=" + b.func1(100, 10));
        System.out.println("50+20+100=" + b.func2(50, 20));
    }
}

输出结果:
5-3=8
100-10=110
50+20+100=170

此时发现原本运行正常的相减功能发生了错误,原因是子类B重写了父类A中的func1方法,从两数相减变成了两数相加。引用基类A完成的功能,换成子类B之后,发生了异常。在实际编程中,我们常常会通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的几率非常大。如果非要重写父类的方法,比较通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合,组合等关系代替。

里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。包含以下4层含义:

  1. 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
  2. 子类中可以增加自己特有的方法。
  3. 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
    当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
  4. 看上去很不可思议,因为我们会发现在自己编程中常常会违反里氏替换原则,程序照样跑的好好的。所以大家都会产生这样的疑问,假如我非要不遵循里氏替换原则会有什么后果?后果就是:你写的代码出问题的几率将会大大增加。
posted @ 2020-11-10 17:19  coolw  阅读(820)  评论(0编辑  收藏  举报