java solid设计原则_六大设计原则之里氏替换原则(LSP)
一、SOLID
设计模式的六大原则有:
Single Responsibility Principle:单一职责原则
Open Closed Principle:开闭原则
Liskov Substitution Principle:里氏替换原则
Law of Demeter:迪米特法则
Interface Segregation Principle:接口隔离原则
Dependence Inversion Principle:依赖倒置原则
把这六个原则的首字母联合起来(两个 L 算做一个)就是 SOLID (solid,稳定的),其代表的含义就是这六个原则结合使用的好处:建立稳定、灵活、健壮的设计。下面我们来看一下里氏替换原则。
设计模式六大原则(SOLID)
二、里氏替换原则定义
【所有引用基类的地方必须能透明地使用其子类的对象】
Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
三、里氏替换原则弥补继承的缺陷
氏替换原则的意思是,所有基类在的地方,都可以换成子类,程序还可以正常运行。这个原则是与面向对象语言的继承特性密切相关的。
在学习java类的继承时,我们知道继承有一些优点:
子类拥有父类的所有方法和属性,从而可以减少创建类的工作量。
提高了代码的重用性。
提高了代码的扩展性,子类不但拥有了父类的所有功能,还可以添加自己的功能。
但有优点也同样存在缺点:
继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法。
降低了代码的灵活性。因为继承时,父类会对子类有一种约束。
增强了耦合性。当需要对父类的代码进行修改时,必须考虑到对子类产生的影响。
如何扬长避短呢?方法是引入里氏替换原则。
四、里氏替换原则对继承进行了规则上的约束
里氏替换原则对继承进行了规则上的约束,这种约束主要体现在四个方面:
子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。
子类中可以增加自己特有的方法。
当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比- 父类方法的输入参数更宽松。(即只能重载不能重写)
当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
下面对以上四个含义进行详细的阐述
4.1、子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法
在我们做系统设计时,经常会设计接口或抽象类,然后由子类来实现抽象方法,这里使用的其实就是里氏替换原则。若子类不完全对父类的方法进行实例化,那么子类就不能被实例化,那么这个接口或抽象类就毫无存在的意义了。
里氏替换原则规定,子类不能覆写父类已实现的方法。父类中已实现的方法其实是一种已定好的规范和契约,如果我们随意的修改了它,那么可能会带来意想不到的错误。下面举例说明一下子类覆写了父类方法带来的后果。
public classFather {public void fun(int a, intb) {
System.out.println(a+ "+" + b + "=" + (a +b));
}
}public class Sun extendsFather {
@Overridepublic void fun(int a, intb) {
System.out.println(a+ "-" + b + "=" + (a -b));
}
}public classClient {public static voidmain(String[] args) {
Father father= newFather();
father.fun(1, 2);//父类存在的地方,可以用子类替代//子类Sun替代父类Father
System.out.println("子类替代父类后的运行结果");
Sun sun= newSun();
sun.fun(1, 2);
}
}
运行结果:
1+2=3子类替代父类后的运行结果1-2=-1
我们想要的结果是“1+2=3”。可以看到,方法重写后结果就不是了我们想要的结果了,也就是这个程序中子类B不能替代父类A。这违反了里氏替换原则,从而给程序造成了错误。
我们可以给父类的非抽象(已实现)方法加final修饰,这样就在语法层面控制了父类非抽象方法被子类重写而违反里氏替换原则。
有时候父类有多个子类(Sun1、Sun2),但在这些子类中有一个特例(Sun2)。要想满足里氏替换原则,又想满足这个子类的功能时,有的伙伴可能会修改父类(Father)的方法。但是,修改了父类的方法又会对其他的子类造成影响,产生更多的错误。这是怎么办呢?我们可以为这个特例(Sun2)创建一个新的父类(Father2),这个新的父类拥有原父类的部分功能(Father2并不继承Father,而是持有Father的一个引用,组合Father,调用Father里的功能),又有不同的功能。这样既满足了里氏替换原则,又满足了这个特例的需求。
4.2、子类中可以增加自己特有的方法
这个很容易理解,子类继承了父类,拥有了父类和方法,同时还可以定义自己有,而父类没有的方法。这是在继承父类方法的基础上进行功能的扩展,符合里氏替换原则。
4.3 、当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松
先看一段代码:
public classFather {public voidfun(HashMap map){
System.out.println("父类被执行...");
}
}public class Sun extendsFather {public voidfun(Map map){
System.out.println("子类被执行...");
}
}public classClient {public static voidmain(String[] args) {
System.out.print("父类的运行结果:");
Father father=newFather();
HashMap map=newHashMap();
father.fun(map);//父类存在的地方,可以用子类替代//子类B替代父类A
System.out.print("子类替代父类后的运行结果:");
Sun sun=newSun();
sun.fun(map);
}
}
运行结果:
父类的运行结果:父类被执行...
子类替代父类后的运行结果:父类被执行...
我们应当主意,子类并非重写了父类的方法,而是重载了父类的方法。因为子类和父类的方法的输入参数是不同的。子类方法的参数Map比父类方法的参数HashMap的范围要大,所以当参数输入为HashMap类型时,只会执行父类的方法,不会执行子类的重载方法。这符合里氏替换原则。
但如果我将子类方法的参数范围缩小会怎样?看代码:
public classFather {public voidfun(Map map){
System.out.println("父类被执行...");
}
}public class Sun extendsFather {public voidfun(HashMap map){
System.out.println("子类被执行...");
}
}public classClient {public static voidmain(String[] args) {
System.out.print("父类的运行结果:");
Father father=newFather();
HashMap map=newHashMap();
father.fun(map);//父类存在的地方,可以用子类替代//子类B替代父类A
System.out.print("子类替代父类后的运行结果:");
Sun sun=newSun();
sun.fun(map);
}
}
运行结果:
父类的运行结果:父类被执行...
子类替代父类后的运行结果:子类被执行...
在父类方法没有被重写的情况下,子方法被执行了,这样就引起了程序逻辑的混乱。所以子类中方法的前置条件必须与父类中被覆写的方法的前置条件相同或者更宽松。
4.4、当子类的方法实现父类的(抽象)方法时,方法的后置条件(即方法的返回值)要比父类更严格
public abstract classFather {public abstractMap fun();
}public class Sun extendsFather {
@OverridepublicHashMap fun() {
System.out.println("子类被执行...");return null;
}
}public classClient {public static voidmain(String[] args) {
Father father=newSun();
father.fun();
}
}
运行结果:
子类被执行...
注意:是实现父类的抽象方法,而不是父类的非抽象(已实现)方法,不然就违法了第一条。
若在继承时,子类的方法返回值类型范围比父类的方法返回值类型范围大,在子类重写该方法时编译器会报错。(Java语法)
java solid设计原则_六大设计原则之里氏替换原则(LSP)相关推荐
- java里氏替换原则例子_java设计模式学习笔记——里氏替换原则
oo中的继承性的思考和说明 1.继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些七月,但是如果子类对这些已经实现的方法任意修改,就会对 ...
- 里氏替换原则_春辉带你了解面相对象设计第二原则(里氏替换原则)
里氏替换原则的定义 里氏替换原则(Liskov Substitution Principle,LSP)由麻省理工学院计算机科学实验室的里斯科夫(Liskov)女士在 1987 年的"面向对象 ...
- 软件设计原则之里氏替换原则、依赖倒置原则
系列文章目录 软件设计原则之单一职责原则.开闭原则 软件设计原则之里氏替换原则.依赖倒置原则 软件设计原则之接口隔离原则.合成复用原则.迪米特原则 文章目录 系列文章目录 一.里氏替换原则 什么是里氏 ...
- 七大设计原则之里氏替换原则应用
目录 1 里氏替换原则 2 里氏替换原则应用 1 里氏替换原则 里氏替换原则(Liskov Substitution Principle,LSP)是指如果对每一个类型为 T1 的对象 o1,都有类型为 ...
- 软件架构设计原则--里氏替换原则
本专栏内容参考自:咕泡学院Tom老师的<Spring5核心原理与30个类手写实战>,仅作个人学习记录使用,如有侵权,联系速删 里氏替换原则(Liskov Substitution Pr ...
- 设计原则之里氏替换原则详解
一.里氏替换原则定义 定义: 如果对每一个类型为T1的对象O1,都有类型为T2的对象O2,使得所有以T1定义的所有程序P在所有的对象O1都替换成O2时,程序P的行为没有发生任何变化,那么类型T2是类型 ...
- 里氏替换原则——举例说明Java设计模式中的里氏替换原则
里氏替换原则--举例说明Java设计模式中的里氏替换原则 1. 前言 官方定义: 2. 举例说明 2.1 例子介绍 2.2 反例 2.2.1 类图说明 2.2.2 代码说明 2.2.3 测试 2.2. ...
- 设计模式原则之里氏替换原则
转载自:https://mp.weixin.qq.com/s/Uq4g53cQ7YKAXP8TuRV2Gw 定义: 1:如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1 ...
- 设计模式-里氏替换原则
设计模式-里氏替换原则 优点 面向对象的语言继承必不可少的,有如下优点 代码共享,减少创建类的工作量 提高代码的重用性 提高代码的可扩展性 提高代码的可扩展性 提高产品代码的开放性 继承侵入性 只要继 ...
最新文章
- node web模块 (服务器端和客户端)
- SystemProperities
- 点击下载!《阿里云SRE技术期刊》2021年5月刊发布啦!
- while、do while练习——7月24日
- 阶梯到XML:1级 - XML简介
- 使用现代化 C# 语法简化代码
- [分布式训练] 单机多卡的正确打开方式:Horovod
- mysql float精度与范围总结
- python函数的使用方法_Python函数使用
- #6277. 数列分块入门 1
- idea java jni 调试_使用 IntelliJ IDEA 和 IntelliJ Clion 进行 JNI 开发
- spring 多租户数据源实现事务一致性
- BM3D算法学习总结
- Git 基础之远程仓库-2.5
- 如何保护自己的个人隐私
- 带你读懂——频率响应与采样频率之间的关系
- 2020-05-11
- py语言和php,php和python什么区别
- 磁盘出现坏道,到底该如何屏蔽坏道?
- 启xin宝app的token算法破解——逆向篇(二)