我们都知道面向对象有三大特性:封装、继承、多态。所以我们在实际开发过程中,子类在继承父类后,根据多态的特性,可能是图一时方便,经常任意重写父类的方法,那么这种方式会大大增加代码出问题的几率。比如下面场景:类C实现了某项功能F1。现在需要对功能F1作修改扩展,将功能F1扩展为F,其中F由原有的功能F1和新功能F2组成。新功能F由类C的子类C1来完成,则子类C1在完成功能F的同时,有可能会导致类C的原功能F1发生故障。这时候里氏替换原则就闪亮登场了。
什么是里氏替换原则
前面说过的单一职责原则,从字面意思就很好理解,但是里氏替换原则就有点让人摸不着头脑。查过资料后发现原来这项原则最早是在1988年,由麻省理工学院一位姓里的女士(Liskov)提出来的。
英文缩写:LSP (Liskov Substitution Principle)。
严格的定义:如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都换成o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型。
通俗的定义:所有引用基类的地方必须能透明地使用其子类的对象。
更通俗的定义:子类可以扩展父类的功能,但不能改变父类原有的功能。
四层含义
里氏替换原则包含以下4层含义:
- 子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。
- 子类中可以增加自己特有的方法。
- 当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
- 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
现在我们可以对以上四层含义逐个讲解。
子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法
在我们做系统设计时,经常会设计接口或抽象类,然后由子类来实现抽象方法,这里使用的其实就是里氏替换原则。子类可以实现父类的抽象方法很好理解,事实上,子类也必须完全实现父类的抽象方法,哪怕写一个空方法,否则会编译报错。
里氏替换原则的关键点在于不能覆盖父类的非抽象方法。父类中凡是已经实现好的方法,实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些规范,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。而里氏替换原则就是表达了这一层含义。
在面向对象的设计思想中,继承这一特性为系统的设计带来了极大的便利性,但是由之而来的也潜在着一些风险。就像开篇所提到的那一场景一样,对于那种情况最好遵循里氏替换原则,类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方法,违背里氏替换原则,引用父类的地方并不能透明的使用子类的对象,导致运行结果出错。
子类中可以增加自己特有的方法
在继承父类属性和方法的同时,每个子类也都可以有自己的个性,在父类的基础上扩展自己的功能。前面其实已经提到,当功能扩展时,子类尽量不要重写父类的方法,而是另写一个方法,所以对上面的代码加以更改,使其符合里氏替换原则,代码如下:
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
当子类覆盖或实现父类的方法时,子类方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松
代码示例
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),因为方法的输入参数不同。重写和重载的区别在Java面向对象详解一文中已作解释,此处不再赘述。
当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格
代码示例:
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=执行子类...}
总结
继承作为面向对象三大特性之一,在给程序设计带来巨大便利的同时,也带来了一些弊端,它增加了对象之间的耦合性。因此在系统设计时,遵循里氏替换原则,尽量避免子类重写父类的方法,可以有效降低代码出错的可能性。
相关推荐
里氏替换原则(Liskov Substitution Principle,简称LSP)是面向对象设计的基本原则之一,它是由美国计算机科学家芭芭拉·里科夫(Barbara Liskov)提出的。这个原则强调的是在软件工程中,子类型必须能够替换它们的...
里氏替换原则(Liskov Substitution Principle, LSP),是由麻省理工学院计算机科学实验室的芭芭拉·里斯科夫(Barbara Liskov)教授在1987年的OOPSLA大会上发表的文章《Data Abstraction and Hierarchy》中首次提出...
3. 里氏替换原则(Liskov Substitution Principle, LSP) 3.1 原则定义 3.2 为什么需要里氏替换原则? 3.3 违反里氏替换原则的例子 3.4 应用里氏替换原则的改进 3.5 现实应用场景 3.6 里氏替换原则的判断标准 3.7 ...
里氏代换原则(Liskov Substitution Principle,简称LSP)是面向对象设计的基本原则之一,由芭芭拉·里科夫(Barbara Liskov)在1988年提出。该原则规定,子类必须能够替换它们的基类,并且在软件系统中不会产生任何...
里氏替换原则(Liskov Substitution Principle, LSP)是面向对象编程设计的基本原则之一,由美国计算机科学家芭芭拉·利斯科夫(Barbara Liskov)于1987年提出。该原则强调了继承在设计类层次结构时的重要性与正确性...
其中,里氏替换原则(Liskov Substitution Principle, LSP)是一项非常重要的原则,它确保了子类可以替换其基类而不破坏程序的正确性。本文将深入探讨里氏替换原则的核心概念及其应用场景,帮助读者理解这一原则的...
里氏替换原则(Liskov Substitution Principle,简称LSP)是面向对象设计的基本原则之一,由Barbara Liskov在1988年提出。该原则指出,子类型必须能够替换它们的基类型而不影响程序的正确性。这意味着在软件系统中,...
2 里氏替换原则-Liskov Substitution Principle (LSP) 3 接口分隔原则-Interface Segregation Principle (ISP) 4 单一职责原则-Single Responsibility Principle (SRP) 5 开闭原则-The Open-Closed ...
2、里氏替换原则【LISKOV SUBSTITUTION PRINCIPLE】:继承与派生的规则.(子类可替换父类) 3、依赖倒转原则【DEPENDENCE INVERSION PRINCIPLE】:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该...
前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第3篇,里氏替换原则LSP(The Liskov Substitution Principle )。 英文原文:...
简单来说,里氏代换原则(LSP,Liskov Substitution Principle)指的是在软件工程中,任何引用基类的地方都能被其子类替代,而不会改变程序原有的行为。这意味着子类应该能够完全无缝地替换掉基类,使得系统在使用...
里氏替换原则(Liskov Substitution Principle,LSP) 只要父类出现的地方都可以用子类替换。 依赖倒置原则(Dependece Inversion Principle,DIP) 面向接口编程。细节应该依赖抽象。 依赖可以传递。 依赖有三...
里氏替换原则(Liskov Substitution Principle,简称LSP)是面向对象设计的基本原则之一,由美国计算机科学家Barbara Liskov在1988年提出。这一原则是类型继承概念的重要基石,它规定了子类型必须能够替换掉它们的基...
里氏替换原则(Liskov Substitution Principle,LSP),是面向对象设计中的一个原则,由芭芭拉·利斯科夫在1988年提出。该原则强调的是子类应该能够替换掉它们的父类,并且不改变程序的正确性。下面我们将详细介绍...
里氏替换原则(Liskov Substitution Principle,LSP):子类应该能够替换掉父类并且工作正常,即子类必须能够完全替代父类的功能而不产生错误。这个原则保证了代码的可靠性和稳定性。 接口隔离原则(Interface ...
里氏替换原则(Liskov Substitution Principle,简称LSP)是面向对象设计的基本原则之一,它在Java设计模式中占据重要地位。该原则由Barbara Liskov于1987年提出,旨在保证软件设计的灵活性和可扩展性。它的核心思想...
里氏代换原则(Liskov Substitution Principle) 里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的...
2、里氏替换原则(Liskov Substitution Principle,LSP) 3、依赖倒置原则(Dependence Inversion Principle,DIP) 4、接口隔离原则(Interface Separate Principle,ISP) 5、合成/聚合复用原则(Composite/...
6. 里氏替换原则(Liskov Substitution Principle) 里氏替换原则是指子类可以替换父类。里氏替换原则可以使代码更灵活、更易维护。 7. 组合优于继承原则(Composition Over Inheritance Principle) 组合优于...