zoukankan      html  css  js  c++  java
  • OOAD-4 面对对象设计七原则--里氏替换原则

    里氏替换原则的定义

      里氏替换原则(Liskov Substitution Principle,LSP)由麻省理工学院计算机科学实验室的里斯科夫(Liskov)女士在 1987 年的“面向对象技术的高峰会议”(OOPSLA)上发表的一篇文章《数据抽象和层次》(Data Abstraction and Hierarchy)里提出来的,她提出:继承必须确保超类所拥有的性质在子类中仍然成立

    里氏替换原则的作用

      里氏替换原则是实现开闭原则的重要方式之一。

      它克服了继承中重写父类造成的可复用性变差的缺点,因为它推崇,子类可以新增方法,但是不要去覆盖父类已经写好的方法

      它是动作正确性的保证,即类的扩展不会给已有的系统引入新的错误。降低了代码出错的可能性。因为里式替换原则保证了子类不要去覆盖父类方法,因此,新增新的子类时。父类定义的东西不会改变。也就是说,进行功能扩展时,原有的代码不会出错

    里式替换原则的实现方法

      里式替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类已经实现的方法。

      如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是用多态用的比较频繁时,程序出错的概率就会变大。因为子类重写毕竟覆盖了父类的功能,当需要使用父类的功能时,就容易导致出错。而且,里氏替换和多态并不冲突,我们尽量在抽象类或者接口来做多态,简单父类一般只是用来自下而上的封装公有域。

      如果程序违背了里式替换原则,则继承类的对象在基类出现的地方会出现运行错误。因为子类重写了方法,那么原本为基类的地方就不一定能用子类来代替了。我们如何判断程序是否违背了里式替换呢。判断方法如下。判断子类是否能继承父类的方法,如果可以继承,则符合里氏替换原则。如果不能继承,则不符合。意思就是,在使用父类的地方把子类代进去,如果方法不会出错,则符合。

      下面以“几维鸟不是鸟”为例来说明里氏替换原则。

    【例2】里氏替换原则在“几维鸟不是鸟”实例中的应用。

    分析:鸟一般都会飞行,如燕子的飞行速度大概是每小时 120 千米。但是新西兰的几维鸟由于翅膀退化无法飞行。假如要设计一个实例,计算这两种鸟飞行 300 千米要花费的时间。显然,拿燕子来测试这段代码,结果正确,能计算出所需要的时间;但拿几维鸟来测试,结果会发生“除零异常”或是“无穷大”,明显不符合预期,其类图如图 1 所示。

    “几维鸟不是鸟”实例的类图
    图1 “几维鸟不是鸟”实例的类图


    程序代码如下:

    1. package principle;
    2. public class LSPtest
    3. {
    4. public static void main(String[] args)
    5. {
    6. Bird bird1=new Swallow();
    7. Bird bird2=new BrownKiwi();
    8. bird1.setSpeed(120);
    9. bird2.setSpeed(120);
    10. System.out.println("如果飞行300公里:");
    11. try
    12. {
    13. System.out.println("燕子将飞行"+bird1.getFlyTime(300)+"小时.");
    14. System.out.println("几维鸟将飞行"+bird2.getFlyTime(300)+"小时。");
    15. }
    16. catch(Exception err)
    17. {
    18. System.out.println("发生错误了!");
    19. }
    20. }
    21. }
    22. //鸟类
    23. class Bird
    24. {
    25. double flySpeed;
    26. public void setSpeed(double speed)
    27. {
    28. flySpeed=speed;
    29. }
    30. public double getFlyTime(double distance)
    31. {
    32. return(distance/flySpeed);
    33. }
    34. }
    35. //燕子类
    36. class Swallow extends Bird{}
    37. //几维鸟类
    38. class BrownKiwi extends Bird
    39. {
    40. public void setSpeed(double speed)
    41. {
    42. flySpeed=0;
    43. }
    44. }


    程序的运行结果如下:

    如果飞行300公里:
    燕子将飞行2.5小时.
    几维鸟将飞行Infinity小时。


    程序运行错误的原因是:几维鸟类重写了鸟类的 setSpeed(double speed) 方法,这违背了里氏替换原则。正确的做法是:取消几维鸟原来的继承关系,定义鸟和几维鸟的更一般的父类,如动物类,它们都有奔跑的能力。几维鸟的飞行速度虽然为 0,但奔跑速度不为 0,可以计算出其奔跑 300 千米所要花费的时间。其类图如图 2 所示。

    “几维鸟是动物”实例的类图
    图2 “几维鸟是动物”实例的类图
     
      
     
     
    里氏替换原则和多态之间的关系
      在第一眼看到里氏替换原则时,我就产生了困惑。为什么要去禁止子类重写父类方法呢。这不是java多态的基本吗,那里式替换原则是不是和多态概念有冲突呢。后来在和朋友交流时候慢慢的有了一些感悟。故而记录下来。
      首先在我们的java语言中。可以用来实现多态的方式有很多种。一般为普通父类、抽象类和接口。而对普通父类而言。它不要用来做多态。普通的父类是自下而上的封装共性域的一种形式。一般而言,我们封装普通父类,是因为有共有的属性、方法。不需要去重写,而是自下而上的封装共性。因此针对普通父类中定义的方法,其实就是子类的共性方法,原理上如果不是共性的方法,就不能抽象为父类。因此普通父类中定义且实现的方法,子类一概不要去重写。这就能看出来里式替换的原理。而多态一般是在抽象类或者接口层去做的。这是因为。抽象类和接口都是自上而下的定义行为约束。例如在抽象类或者接口中,定义的抽象方法,并无具体实现,这就是表明子类必须要去重写该方法来实现多态。而针对抽象类或者接口中已经定义且做了实现的方法,则就是共性定义的方法。继承抽象类或者实现接口的类都不应该去覆盖该方法,就直接拿来用就好了。这也是里氏替换原则的体现
     
  • 相关阅读:
    Python 多线程学习(转)
    自己使用python webob,paste.deploy,wsgi总结
    Python中*args 和**kwargs的用法
    python 数字和字符串转换问题
    python socket编程
    深入解读Quartz的原理
    解决get方法传递URL参数中文乱码和解决tomcat下中文乱码问题
    Tomcat的Manager显示403 Access Denied
    mysql5.6 linux下安装笔记
    Quartz应用与集群原理分析
  • 原文地址:https://www.cnblogs.com/exceptionblog/p/11044179.html
Copyright © 2011-2022 走看看