什么是里氏替换原则
什么是里氏替换原则
什么是里氏替换原则一、继承的弊端
继承作为面向对象三大特性之一,在给程序设计带来巨大便利的同时,也带来了弊端。例如
1、继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法;
2、降低代码的灵活性。子类必须拥有父类的属性和方法,让子类自由的世界中多了些约束;
3、增强了耦合性。当父类的常量、变量和方法被修改时,必需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果——大片的代码需要重构。
二、里氏替换原则的定义
1、里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。
2、里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。
3、里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。
三、里氏替换原则包含的含义
1、子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法
在我们做系统设计时,经常会设计接口或抽象类,然后由子类来实现抽象方法,这里使用的其实就是里氏替换原则。子类可以实现父类的抽象方法很好理解,事实上,子类也必须完全实现父类的抽象方法,哪怕写一个空方法,否则会编译报错。
里氏替换原则的关键点在于不能覆盖父类的非抽象方法。父类中凡是已经实现好的方法,实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些规范,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。而里氏替换原则就是表达了这一层含义。
在面向对象的设计思想中,继承这一特性为系统的设计带来了极大的便利性,但是由之而来的也潜在着一些风险。所以,类C1继承类C时,可以添加新方法完成新增功能,尽量不要重写父类C的方法。否则可能带来难以预料的风险。
例如
public class C {
public int func(int a, int b){
return a+b;
}
}
public class C1 extends C{
@Override
public int func(int a, int b) {
return a-b;
}
}
public class Client{
public static void main(String[] args) {
C c = new C1();
System.out.println("2+1=" + c.func(2, 1));
}
}
运行结果:2+1=1
上面的运行结果明显是错误的。类C1继承C,后来需要增加新功能,类C1并没有新写一个方法,而是直接重写了父类C的func方法,违背里氏替换原则,引用父类的地方并不能透明的使用子类的对象,导致运行结果出错。
2、子类中可以增加自己特有的方法
在继承父类属性和方法的同时,每个子类也都可以有自己的个性,在父类的基础上扩展自己的功能。前面其实已经提到,当功能扩展时,子类尽量不要重写父类的方法,而是另写一个方法,所以对上面的代码加以更改,使其符合里氏替换原则
例如
public class C {
public int func(int a, int b){
return a+b;
}
}
public class C1 extends C{
public int func2(int a, int b) {
return a-b;
}
}
public class Client{
public static void main(String[] args) {
C1 c = new C1();
System.out.println("2-1=" + c.func2(2, 1));
}
}
运行结果:2-1=1
3、当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松
例如
import java.util.HashMap;
public class Father {
public void func(HashMap m){
System.out.println("执行父类...");
}
}
import java.util.Map;
public class Son extends Father{
public void func(Map m){//方法的形参比父类的更宽松
System.out.println("执行子类...");
}
}
import java.util.HashMap;
public class Client{
public static void main(String[] args) {
Father f = new Son();//引用基类的地方能透明地使用其子类的对象。
HashMap h = new HashMap();
f.func(h);
}
}
运行结果:执行父类...
注意Son类的func方法前面是不能加@Override注解的,因为否则会编译提示报错,因为这并不是重写(Override),而是重载(Overload),因为方法的输入参数不同。
4、当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格
例如
import java.util.Map;
public abstract class Father {
public abstract Map func();
}
import java.util.HashMap;
public class Son extends Father{
@Override
public HashMap func(){//方法的返回值比父类的更严格
HashMap h = new HashMap();
h.put("h", "执行子类...");
return h;
}
}
public class Client{
public static void main(String[] args) {
Father f = new Son();//引用基类的地方能透明地使用其子类的对象。
System.out.println(f.func());
}
}
执行结果:{h=执行子类...}
四、在使用里氏代换原则时需要注意如下几个问题
1、子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。根据里氏代换原则,为了保证系统的扩展性,在程序中通常使用父类来进行定义,如果一个方法只存在子类中,在父类中不提供相应的声明,则无法在以父类定义的对象中使用该方法。
2、我们在运用里氏代换原则时,尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法,运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能,同时无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。里氏代换原则是开闭原则的具体实现手段之一。
3、在系统设计时,遵循里氏替换原则,尽量避免子类重写父类的方法,可以有效降低代码出错的可能性。
- vue基础语法对象(浅析从面向对象思维理解Vue组件)
- python学生信息管理系统教程(学生信息管理系统Python面向对象版)
- php使用面向对象如何写好代码(PHP面向对象程序设计模拟一般面向对象语言中的方法重载overload示例)
- php 面向对象与面向过程(php面向对象重点知识分享)
- javascript对象添加方法(详解JavaScript面向对象实战之封装拖拽对象)
- php面向对象运用场景(PHP面向对象类型约束用法分析)
- php面向对象怎么用(PHP面向对象程序设计之对象克隆clone和魔术方法__clone用法分析)
- python内置函数一览表(Python面向对象程序设计构造函数和析构函数用法分析)
- php面向对象怎么调用属性(PHP面向对象程序设计__tostring和__invoke用法分析)
- python中对象方法和顶级方法(Python3.5面向对象程序设计之类的继承和多态详解)
- python图书管理系统(python面向对象法实现图书管理系统)
- python面向对象基本思想(详解Python:面向对象编程)
- python支持面向对象的程序设计(Python面向对象程序设计之类的定义与继承简单示例)
- 面向对象设计中的开放封闭原则
- php面向对象3大特征(PHP面向对象程序设计重载overloading操作详解)
- php面向对象教程理解(PHP面向对象程序设计之构造方法和析构方法详解)
- 苹果自研芯片跑分对比 A16芯片排名靠后,M1系列霸榜(苹果自研芯片跑分对比)
- X86处理器的梦魇 苹果M1自研芯片到底有多强(苹果M1自研芯片到底有多强)
- 泰剧《爱欲之神》Boom kitkong和Great合体杂志(泰剧爱欲之神Boomkitkong和Great合体杂志)
- 素人恋爱综艺火药味十足 男生为赢得芳心集体扯头花,真是出好戏(素人恋爱综艺火药味十足)
- 《囧妈》为何受抵制 春节七部影片撤档背后的责任与博弈(囧妈为何受抵制)
- 提醒 2019年起河南驾考要开设科目五 官方回应来了(2019年起河南驾考要开设科目五)
热门推荐
- django部署(Django 中间键和上下文处理器的使用)
- python转pdf教程(Python实现将HTML转成PDF的方法分析)
- python四舍五入怎么用(python3 小数位的四舍五入用两种方法解决round 遇5不进)
- dell r730服务器安装操作系统(DELL R730服务器配置RAID与安装服务器系统以及域的控制详细图文教程)
- pandas数据分组使用方法(在Pandas中DataFrame数据合并,连接concat,merge,join的实例)
- 怎样使用python图像处理(Python Image模块基本图像处理操作小结)
- dedecms分页效果(dedecms重新定义cn_substr函数截取字数更准确)
- mysql连接navicat报错1045(Navicat 连接MySQL8.0.11出现2059错误)
- mysqlworkbench怎么设置连接(详解MySQL Workbench使用教程)
- php7.4类型属性实例详解(PHP 7.4中使用预加载的方法详解)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9