举例解析Java的设计模式编程中里氏替换原则的意义

 更新时间:2016年02月03日 10:41:47   作者:卡奴达摩  
这篇文章主要介绍了Java的设计模式中里氏替换原则的意义,文中举例来说明里氏替换原则中强调的继承特性方面可能带来的问题,需要的朋友可以参考下

里氏替换原则,OCP作为OO的高层原则,主张使用“抽象(Abstraction)”和“多态(Polymorphism)”将设计中的静态结构改为动态结构,维持设计的封闭性。“抽象”是语言提供的功能。“多态”由继承语义实现。

里氏替换原则包含以下4层含义:

  1. 子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。
  2. 子类中可以增加自己特有的方法。
  3. 当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
  4. 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

  现在我们可以对以上四层含义进行讲解。

  子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法

  在我们做系统设计时,经常会设计接口或抽象类,然后由子类来实现抽象方法,这里使用的其实就是里氏替换原则。子类可以实现父类的抽象方法很好理解,事实上,子类也必须完全实现父类的抽象方法,哪怕写一个空方法,否则会编译报错。

  里氏替换原则的关键点在于不能覆盖父类的非抽象方法。父类中凡是已经实现好的方法,实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些规范,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。而里氏替换原则就是表达了这一层含义。

  在面向对象的设计思想中,继承这一特性为系统的设计带来了极大的便利性,但是由之而来的也潜在着一些风险。下面举例来说明继承的风险,我们需要完成一个两数相减的功能,由类A来负责。

class A{ 
  public int func1(int a, int b){ 
    return a-b; 
  } 
} 
 
public class Client{ 
  public static void main(String[] args){ 
    A a = new A(); 
    System.out.println("100-50="+a.func1(100, 50)); 
    System.out.println("100-80="+a.func1(100, 80)); 
  } 
} 

 运行结果:

100-50=50
100-80=20

        后来,我们需要增加一个新的功能:完成两数相加,然后再与100求和,由类B来负责。即类B需要完成两个功能:
两数相减。
两数相加,然后再加100。
        由于类A已经实现了第一个功能,所以类B继承类A后,只需要再完成第二个功能就可以了,代码如下:

class B extends A{ 
  public int func1(int a, int b){ 
    return a+b; 
  } 
   
  public int func2(int a, int b){ 
    return func1(a,b)+100; 
  } 
} 
 
public class Client{ 
  public static void main(String[] args){ 
    B b = new B(); 
    System.out.println("100-50="+b.func1(100, 50)); 
    System.out.println("100-80="+b.func1(100, 80)); 
    System.out.println("100+20+100="+b.func2(100, 20)); 
  } 
} 

类B完成后,运行结果:

100-50=150
100-80=180
100+20+100=220

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


相关文章

  • Java 批量删除html中注释内容的方法

    Java 批量删除html中注释内容的方法

    最近项目中有一个功能需要读取外部html文本文件。但是有的html文件里面有大量的注释,需要删除其中的注释在存储
    2014-04-04
  • Spring Boot报错:No session repository could be auto-configured, check your configuration的解决方法

    Spring Boot报错:No session repository could be auto-configu

    这篇文章主要给大家介绍了关于Spring Boot报错:No session repository could be auto-configured, check your configuration的解决方法,文中给出了详细的解决方法,对遇到这个问题的朋友们具有一定参考价值,需要的朋友下面来一起看看吧。
    2017-07-07
  • Java如何防止JS脚本注入代码实例

    Java如何防止JS脚本注入代码实例

    这篇文章主要介绍了Java如何防止JS脚本注入代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • Java中数组协变和范型不变性踩坑记录

    Java中数组协变和范型不变性踩坑记录

    数组的协变性来源于数组的一个优势,这篇文章主要给大家介绍了关于Java中数组协变和范型不变性踩坑的一些内容,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-02-02
  • java实现发送邮件功能

    java实现发送邮件功能

    这篇文章主要为大家详细介绍了java实现发送邮件功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-02-02
  • IntelliJ IDEA连接MySQL数据库详细图解

    IntelliJ IDEA连接MySQL数据库详细图解

    今天小编就为大家分享一篇关于intellij idea连接mysql数据库详细图解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-10-10
  • 解决IDEA2020.2插件lombok报错问题(亲测有效)

    解决IDEA2020.2插件lombok报错问题(亲测有效)

    这篇文章主要介绍了解决IDEA2020.2插件lombok报错问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • Java创建子线程的两种方法

    Java创建子线程的两种方法

    这篇文章主要介绍了Java创建子线程的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • 简介Java的Hibernate框架中的Session和持久化类

    简介Java的Hibernate框架中的Session和持久化类

    这篇文章主要介绍了Java的Hibernate框架中的Session和持久化类,Hibernate是Java的SSH三大web开发框架之一,需要的朋友可以参考下
    2015-12-12
  • 解决MyEclipse中的Building workspace问题的三个方法

    解决MyEclipse中的Building workspace问题的三个方法

    这篇文章主要介绍了解决MyEclipse中的Building workspace问题的三个方法,需要的朋友可以参考下
    2015-11-11

最新评论