肯定有不少人跟我刚看到这项原则的时候一样,对这个原则的名字充满疑惑。事实上原因就是这项原则最早是在1988年,由麻省理工学院的一位姓里的女士(Barbara Liskov)提出来的。

定义1:假设对每个类型为 T1的对象 o1,都有类型为 T2 的对象o2。使得以 T1定义的全部程序 P 在全部的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。

定义2:全部引用基类的地方必须能透明地使用其子类的对象。

问题由来:有一功能P1,由类A完毕。现须要将功能P1进行扩展,扩展后的功能为P,当中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完毕,则子类B在完毕新功能P2的同一时候,有可能会导致原有功能P1发生问题。

解决方式:当使用继承时,遵循里氏替换原则。类B继承类A时,除加入新的方法完毕新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。

继承包括这样一层含义:父类中凡是已经实现好的方法(相对于抽象方法而言),实际上是在设定一系列的规范和契约,尽管它不强制要求全部的子类必须遵从这些契约,可是假设子类对这些非抽象方法随意改动,就会对整个继承体系造成破坏。而里氏替换原则就是表达了这一层含义。

继承作为面向对象三大特性之中的一个,在给程序设计带来巨大便利的同一时候,也带来了弊端。

比方使用继承会给程序带来侵入性,程序的可移植性减少。添加了对象间的耦合性,假设一个类被其它的类所继承。则当这个类须要改动时,必须考虑到全部的子类。而且父类改动后,全部涉及到子类的功能都有可能会产生故障。

举例说明继承的风险,我们须要完毕一个两数相减的功能,由类A来负责。

1
2
3
4
5
6
7
8
9
10
11
12
13
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后,仅仅须要再完毕第二个功能就能够了,代码例如以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
classBextendsA{
    publicintfunc1(inta,intb){
        returna+b;
    }
    
    publicintfunc2(inta,intb){
        returnfunc1(a,b)+100;
    }
}
 
publicclassClient{
    publicstaticvoidmain(String[]args){
        Bb=newB();
        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之后,发生了异常。

在实际编程中,我们经常会通过重写父类的方法来完毕新的功能,这样写起来尽管简单。可是整个继承体系的可复用性会比較差,特别是运用多态比較频繁时,程序执行出错的几率很大。假设非要重写父类的方法,比較通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,採用依赖、聚合,组合等关系取代。

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

  • 子类能够实现父类的抽象方法,但不能覆盖父类的非抽象方法。
  • 子类中能够添加自己特有的方法。

  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的形參)要比父类方法的输入參数更宽松。
  • 当子类的方法实现父类的抽象方法时。方法的后置条件(即方法的返回值)要比父类更严格。

看上去非常不可思议,由于我们会发如今自己编程中经常会违反里氏替换原则,程序照样跑的好好的。

所以大家都会产生这种疑问,假如我非要不遵循里氏替换原则会有什么后果?

后果就是:你写的代码出问题的几率将会大大添加。

设计模式六大原则(2):里氏替换原则(Liskov Substitution Principle)的更多相关文章

  1. IOS设计模式的六大设计原则之里氏替换原则(LSP,Liskov Substitution Principle)

    定义 里氏替换原则的定义有两种,据说是由麻省理工的一位姓里的女士所提出,因此以其名进行命名. 定义1:如果对一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1所定义的程序P中在o1全都替换 ...

  2. Java设计模式(4:里氏替换原则和合成复用原则详解

    一.里氏替换原则 如果说实现开闭原则的关键步骤就是抽象化,那么基类(父类)和子类的继承关系就是抽象化的具体实现,所以里氏替换原则就是对实现抽象化的具体步骤的规范.即:子类可以扩展基类(父类)的功能,但 ...

  3. [设计模式]<<设计模式之禅>>关于里氏替换原则

    在面向对象的语言中,继承是必不可少的.非常优秀的语言机制,它有如下优点:● 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性:● 提高代码的重用性:● 子类可以形似父类,但又异于父类,“龙 ...

  4. 设计模式课程 设计模式精讲 3-10 里氏替换原则coding

    1 代码演练 1.1 继承关系判别(是否是真正意义的继承) 1.2 入参控制 1.3 出参控制 1 代码演练 1.1 继承关系判别(是否是真正意义的继承)(其实我觉得这个例子有点牵强) 1.1.1 反 ...

  5. Java设计原则之里氏替换原则

    里氏代换原则由2008年图灵奖得主.美国第一位计算机科学女博士Barbara Liskov教授和卡内基·梅隆大学Jeannette Wing教授于1994年提出.其严格表述如下:如果对每一个类型为S的 ...

  6. 深入理解JavaScript系列(8):S.O.L.I.D五大原则之里氏替换原则LSP

    前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第3篇,里氏替换原则LSP(The Liskov Substitution Principle ). 英文原文:http ...

  7. 北风设计模式课程---里氏替换原则(Liskov Substitution Principle)

    北风设计模式课程---里氏替换原则(Liskov Substitution Principle) 一.总结 一句话总结: 当衍生类能够完全替代它们的基类时:(Liskov Substitution P ...

  8. 【C#设计模式】里氏替换原则

    今天,我们再来学习 SOLID 中的"L"对应的原则:里式替换原则. 里氏替换原则 里氏替换原则(Liskov Substitution Principle):派生类(子类)对象能 ...

  9. [OOD]违反里氏替换原则的解决方案

    关于OOD中的里氏替换原则,大家耳熟能祥了,不再展开,可以参考设计模式的六大设计原则之里氏替换原则.这里尝试讨论常常违反的两种形式和解决方案. 违反里氏替换原则的根源是对子类及父类关系不明确.我们在设 ...

  10. C# 实例解释面向对象编程中的里氏替换原则

    在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解.灵活和可维护.这些原则是由美国软件工程师和讲师罗伯特·C·马丁(Robert Cecil Martin)提出的许多原 ...

随机推荐

  1. 【Web】java异常处理

    J2EE中一般对异常状况的处理都可以用两种情况对其进行相应处理. 1. 通常情况下,一般异常处理可以选择用throw.throws从底层一直往上面抛,直到抛到Action,让其将异常显示在页面上面进行 ...

  2. [转] Ant 编译 Android 项目为 Apk 实战, 常见问题解决

    补充,自行安装Ant, 配置ant环境变量, 在android 项目 根目录下使用 android update project 可以自动生成 build.xml 和 local.peoperties ...

  3. tomcat编译通过问题

    tomcat  编译后 的类 和 网站目录不能同名!

  4. hadoop -- mapreduce执行过程

    1.运行mapreduce程序  ---run2.本次运行将会生成呢个一个Job , 于是JobClient向JobTracker申请一个JobID 标识该Job.3.JobClient将Job需要的 ...

  5. VirtualBox中的Ubuntu没有权限访问共享文件夹/media/sf_bak

    之前已经搞定可以自动共享文件夹了,但是现在发现无法去访问,非root用户下,使用“ls /media/sf_bak”提示没有权限,当然如果切换到root,是可以的. [解决过程]1.把普通用户名加入到 ...

  6. JSON数组分配输出每个li

    有这么一个JSON数组,需求是只需要输出每个数组里面的某个值,不需要全部输出来. var data = [ { ", "Cost":"13,642.41&quo ...

  7. Error creating bean with name 'org.springframework.validation.beanvalidation.LocalValidatorFactory

    Error creating bean with name ‘org.springframework.validation.beanvalidation.LocalValidatorFactoryBe ...

  8. oschina应用工具

    应用工具 22思维导图软件 9博客客户端 15加密/解密/破解 120浏览器 78邮件工具 327文档/文本编辑 31杀毒软件 177终端/远程登录 195IM/聊天/语音工具 74下载工具 64文件 ...

  9. 由于物化视图定义为on commit导致update更新基表慢的解决方案

    由于物化视图定义为on commit导致update更新基表慢的解决方案 以下是模拟和解决测试过程: (模拟update慢的过程) 1.首先基于基表创建物化视图日志: create materiali ...

  10. java项目打jar包的两种情况

    链接地址:http://jingyan.baidu.com/article/6b97984d8a6ddc1ca2b0bfa0.html 本文介绍一下java项目打jar包时的两种情况各怎么操作   方 ...