• 02-里氏替换原则(LSP)


    1. 背景

           有一个功能p1,由类A完成,现在需要将功能p1进行扩展,扩展后的功能为p3,p3由原功能p1和新功能p2组成,而新功能p3和p2均由类A的子类B来完成,子类B在完成新功能p2的同时,可能会导致原有的功能p1故障。

    2. 定义

        所有引用基类的地方能透明的使用其子类对象进行替代。

    3. 对应的解决方案

      使用继承时,父类已经实现好了的方法(非抽象方法),实际上是有一系列规范和契约的,虽然不强制子类必须遵循,但如果子类对这些非抽象方法进行重写、重载后,会造成整个继承体系被破坏。而里氏替换原则也正是表达了这层含义。

    4. 补充对继承的理解

       继承作为面向对象三大特性之一,在给程序设计带来巨大便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加了对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能会产生故障。

    5.  案例

        需求一:完成两个数相减的功能,由类A完成。

     1  public class A
     2     {
     3         /// <summary>
     4         /// 两数相加
     5         /// </summary>
     6         /// <param name="a"></param>
     7         /// <param name="b"></param>
     8         /// <returns></returns>
     9         public int func1(int a, int b)
    10         {
    11             return a - b;
    12         }
    13         /// <summary>
    14         /// 两数相乘
    15         /// </summary>
    16         /// <param name="a"></param>
    17         /// <param name="b"></param>
    18         /// <returns></returns>
    19         public int func3(int a, int b)
    20         {
    21             return a * b;
    22         }
    23     }

        需求二:完成两个数相加,然后再加100的功能,由类A的子类类B完成。

     1   public class B:A
     2     {
     3         public int func1(int a, int b)
     4         {
     5             return a + b;
     6         }
     7         public int func2(int a, int b)
     8         {
     9             return func1(a, b) + 100;
    10         }
    11     }

     下面我们对上述写的代码进行测试。

        (1). 用类A测试两个数相减,如:100-50

        (2). 用类B测试两个数相减(因为类B继承了类A),如:100-50

        (3). 用类B测试两个数相加后再加100的功能,如:100+50+100

     1         public static void show()
     2         {
     3             //需求1:完成两个数相减的功能,由类A来完成
     4             A a = new A();
     5             Console.WriteLine("100-50={0}", a.func1(100, 50));   //50
     6 
     7             //需求2:完成两个数相加功能,然后再加100,由类A的子类B来完成
     8             B b = new B();
     9             Console.WriteLine("100-50={0}", b.func1(100, 50));   //150,因为子类重写了fun1,则隐藏了父类中fun1原有的方法 (错误)
    10             Console.WriteLine("100和50相加,然后再减掉100,结果为:{0}", b.func2(100, 50));   //250
    11 
    12         }

     结果:

      

       我们会发现, 用类B测试两个数相减的功能出错了,竟然变成两数相加的功能了,原因是类B中声明了和其父类相同的方法名的方法,即隐藏了父类原有的方法,违背了里氏替换原则。

    6. 里氏替换原则的深刻理解

     子类可以扩展父类没有的功能,但是不能改变父类的功能

    a:子类可以实现父类的抽象方法,但不能重写父类的非抽象方法。

    b:子类可以增加自己特有的方法。

    c:子类重载父类方法时,其方法的形参要比父类方法的形参更宽松。

    d:子类实现父类抽象方法时,其方法的返回值要比父类方法的更严格。

    7. 正宗的里氏替换原则的用法

            public static void show()
            {
    
                A aa = new B();
                Console.WriteLine("100*50={0}",aa.func3(100, 50));   //5000   正宗的里氏替换原则
                //Console.WriteLine(aa.func2(100, 50));   //代码报错,父类不能调用子类扩展的方法
            }

    8. 违背里氏替换原则的后果

     出错率会增高

  • 相关阅读:
    Android数据存储
    linux中uptime命令查看linux系统负载
    Linux系统中的load average
    Trie树(c++实现)
    模版
    重载操作符[]
    stanford-postagger中文词性标注
    数值的整数次方(剑指offer面试题11)
    __init__ 和 self
    python中文分词:结巴分词
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/7101345.html
Copyright © 2020-2023  润新知