zoukankan      html  css  js  c++  java
  • 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. 看上去很不可思议,因为我们会发现在自己编程中常常会违反里氏替换原则,程序照样跑的好好的。所以大家都会产生这样的疑问,假如我非要不遵循里氏替换原则会有什么后果?后果就是:你写的代码出问题的几率将会大大增加。
  • 相关阅读:
    mysql把一个表的字段update成另一个表的字段根据id
    1月房地产企业销售TOP100出炉 万科重回第一
    别拿学历说事,这些天王中学没毕业,最低的是成龙
    马云:未来经济形势很艰难,需要企业家挺身而出
    私有云与公有云区别,公有云不可能统一天下
    除了首付,购房预算还须有这7项才能买到房!
    房产交易中的10个非典型问题 你肯定不懂!
    几个常用快速无损压缩算法性能比较
    ValueTask
    Mobaxterm
  • 原文地址:https://www.cnblogs.com/lwcode6/p/13954911.html
Copyright © 2011-2022 走看看