肯定有不少人跟我刚看到这项原则的时候一样,对这个原则的名字充满疑惑。事实上原因就是这项原则最早是在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. Mysql 的一些基本用法

    一.增加字段 COMMENT '是否导入基础信息平台 1 是导入'; 二.删除字段 alter table `provincestudentinfo` drop column NativePlace; ...

  2. 【转】 /etc/fstab功能详解

    [转] /etc/fstab功能详解 最近去客户现场时,遇到 了一个关于挂载文件/etc/fstab文件的问题,就写了一下/etc/fstab文件的作用一个文件中各个参数的含义.供大家参考有不正确的地 ...

  3. [转]Java Code Examples for android.util.JsonReader

    [转]Java Code Examples for android.util.JsonReader The following are top voted examples for showing h ...

  4. BZOJ 2654: tree( 二分 + MST )

    我们给白色的边增加权值 , 则选到的白色边就会变多 , 因此可以二分一下. 不过这道题有点小坑... ------------------------------------------------- ...

  5. GitHub的问题

    出现failed to publish the branch, 转自:http://blog.csdn.net/cucmakeit/article/details/29407329 (windows系 ...

  6. HDU 5012 Dice DFS

    简单DFS //#pragma comment(linker, "/STACK:16777216") //for c++ Compiler #include <stdio.h ...

  7. Gauss elimination Template

    Gauss elimination : #include <iostream> #include <cstdlib> #include <cstring> #inc ...

  8. QML在XP等显卡明显不好的情况下 可以参考

    http://doc.qt.io/qt-5/windows-requirements.html

  9. 练习 jquery+Ajax+Json 绑定数据 分类: asp.net 练习 jquery+Ajax+Json 绑定数据 分类: asp.net

    练习 jquery+Ajax+Json 绑定数据

  10. [概率dp] ZOJ 3822 Domination

    题意: 给N×M的棋盘.每天随机找一个没放过棋子的格子放一个棋子 问使得每一个每列都有棋子的天数期望 思路: dp[i][j][k] 代表放了i个棋子占了j行k列 到达目标状态的期望 然后从 dp[n ...