基本介绍:
里氏替换原则在1988年,由麻省理工学院的一位姓里的女士提出的
她提出了这么一个思想:如果对每个类型为T1的对象o1,都有类型为T2的对象o2,是的以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有应用基类的地方必须能透明地使用其子类的对象。
在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖来解决问题。
应用案例:
说明:
A类中有一个func1的方法求两数之差
B类继承A类,但是要新增一个功能,求两数之和再和9相加
1 package cn.rabcheng.liskovsubstitution; 2 3 /** 4 * @auther cheng 5 * @create 2020-08-15 10:58 6 * 里氏替换原则 7 */ 8 public class LiskovSubstitution { 9 10 public static void main(String[] args) { 11 12 A a = new A(); 13 System.out.println("11 - 3 = " + a.func1(11,3)); 14 System.out.println("1 - 8 = " + a.func1(1,8)); 15 16 System.out.println("====================="); 17 18 B b = new B(); 19 System.out.println("11 - 3 = " + b.func1(11,3));//本意是调用父类的方法求出11-3 20 System.out.println("1 - 8 = " + b.func1(1,8));//本意是调用父类的方法求出1-8 21 System.out.println("11 + 3 + 9 = "+ b.func2(11,3)); 22 23 } 24 } 25 26 class A{ 27 //返回两个数的差 28 public int func1(int num1,int num2){ 29 return num1 - num2; 30 } 31 } 32 33 //B类的本意是新增一个功能: 34 //增加一个两数相加的功能,然后再和9求和 35 //无意间第一个方法名写的和A类的方法名一样了,参数也一样相当于重写了A类额方法 36 class B extends A{ 37 38 public int func1(int num1,int num2){ 39 return num1 + num2; 40 } 41 42 public int func2(int a,int b){ 43 return func1(a,b) + 9; 44 } 45 46 } 47 11 - 3 = 8 48 1 - 8 = -7 49 ===================== 50 11 - 3 = 14 51 1 - 8 = 9 52 11 + 3 + 9 = 23
结果和预期的结果不满足,程序发生了改变。
发现原来运行正常的程序相减功能发生了错误,原因就是B类中无意重写了父类A中的func1方法,造成原油功能出现错误,在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但是整个继承体系的复用性会比较差,特别是运行多台比较频繁的时候
通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合、组合灯关系替代。
解决方法:
创建一个更加基础的基类,把更加基础的方法和成员写到Base类
让类A和类B分别继承Base类
使用组合的方式调用A中的方法
1 package cn.rabcheng.liskovsubstitution; 2 3 /** 4 * @auther cheng 5 * @create 2020-08-15 11:19 6 */ 7 public class LiskovSubstitution2 { 8 9 public static void main(String[] args) { 10 A2 a = new A2(); 11 System.out.println("11 - 3 = " + a.func1(11,3)); 12 System.out.println("1 - 8 = " + a.func1(1,8)); 13 14 System.out.println("====================="); 15 16 B2 b = new B2(); 17 System.out.println("11 + 3 = " + b.func1(11,3));//本意是调用父类的方法求出11-3 18 System.out.println("1 + 8 = " + b.func1(1,8));//本意是调用父类的方法求出1-8 19 System.out.println("11 + 3 + 9 = "+ b.func2(11,3)); 20 21 //使用组合调用A类方法 22 System.out.println("11 - 3 = " + b.func3(11,3)); 23 } 24 } 25 26 //创建一个更加基础的基类 27 class Base{ 28 //把更更加基础的方法和成员写到Base类中 29 } 30 31 class A2 extends Base{ 32 //返回两个数的差 33 public int func1(int num1,int num2){ 34 return num1 - num2; 35 } 36 } 37 38 class B2 extends Base{ 39 public int func1(int num1,int num2){ 40 return num1 + num2; 41 } 42 43 public int func2(int a,int b){ 44 return func1(a,b) + 9; 45 } 46 47 //如果B类需使用A类的方法,使用组合关系 48 private A2 a = new A2(); 49 50 //使用A2中的方法 51 public int func3(int a,int b){ 52 return this.a.func1(a,b); 53 } 54 } 55 11 - 3 = 8 56 1 - 8 = -7 57 ===================== 58 11 + 3 = 14 59 1 + 8 = 9 60 11 + 3 + 9 = 23 61 11 - 3 = 8
继承包含这样一层含义,父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现好的方法任意修改,就会对整个继承体系造成破坏。
继承在给程序设计带来便利的同时,也带来了弊端,比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他类所继承,泽当这个类需要修改时,必须考虑到所有的子类,并且谷类修改后,所有涉及到子类的功能都有可能产生故障
所以,在编程中如果要使用继承,需要遵循里氏替换原则