首页 > 技术文章 > 设计模式七大原则-里氏替换原则

Rabcheng 2020-08-15 11:37 原文

基本介绍:

里氏替换原则在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

 

继承包含这样一层含义,父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现好的方法任意修改,就会对整个继承体系造成破坏。

继承在给程序设计带来便利的同时,也带来了弊端,比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他类所继承,泽当这个类需要修改时,必须考虑到所有的子类,并且谷类修改后,所有涉及到子类的功能都有可能产生故障

所以,在编程中如果要使用继承,需要遵循里氏替换原则

 

推荐阅读