"围观"设计模式--里氏替换原则(LSP,Liskov Substitution Principle)

Posted slgkaifa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了"围观"设计模式--里氏替换原则(LSP,Liskov Substitution Principle)相关的知识,希望对你有一定的参考价值。

面向对象的程序设计中。里氏替换原则(Liskov Substitution principle)是对子类型的特别定义。它由芭芭拉·利斯科夫(Barbara Liskov)在1987年在一次会议上名为“数据的抽象与层次”的演说中首先提出。

里氏替换原则的内容能够描写叙述为: “派生类(子类)对象能够替换其基类(超类)对象被使用。” 以上内容并不是利斯科夫的原文,而是译自罗伯特·马丁(Robert Martin)对原文的解读。

其原文为:

Let 技术分享 be a property provable about objects技术分享 of type技术分享. Then技术分享 should be true for objects技术分享 of type技术分享 where技术分享 is a subtype of技术分享.

芭芭拉·利斯科夫与周以真(Jeannette Wing)在1994年发表论文并提出的以上的Liskov代换原则。----维基百科


里氏替换原则我个人的理解是:在继承关系中,父类的对象假设替换为子类的对象,他原来运行的行为依旧保持不变,那么这种程序才符合里氏替换原则,否则违背了里氏替换原则。


以下我们看这样一个实例,体会下,里氏替换原则是在什么情况下违背的。


技术分享

一个简单的继承结构,在子类中,重写父类的方法calc方法。

父类Calc:

package cn.design.pattern2016032004LiskovSubstitutionPrinciple;

public class Calc {

	public void calc(int a, int b) {
		// a-b = ?
		System.out.println(a + " - " + b + " = " + (a - b));
		
	}
}

子类CalcSon,通过将父类中calc这种方法重写为两个数相加。

package cn.design.pattern2016032004LiskovSubstitutionPrinciple;

public class CalcSon extends Calc{

	public void calc(int a, int b) {
		// a+b = ?
		System.out.println(a + " + " + b + " = " + (a + b));
	}
	
	// other method
	public void addThem(int a, int b) {
		System.out.println(a + b);
	}
}

測试类:这里假设符合里氏替换原则的话,那么应该说将父类的调用的这个地方直接换为子类的话,那么原来的行为不会发生不论什么的改变。

可是以下的程序证明了,这种做法是违背了里氏替换原则的。将原先父类调用的替换为子类的时候。会由原来的父类的方法:减法。变为如今子类中的:加法。结果发生变化,从而违背了里氏替换原则。

Calc cal = new Calc();
cal.calc(10, 20);
		
/**
* 依据里氏替换原则。当父类替换为子类的时候,使用父类的时候的行为不应该
* 发生变化,那么以下的这段代码,显然发生了变化,这样显然违反了里氏替换
* 原则。
*/
Calc calcSon = new CalcSon();
calcSon.calc(10, 20);

我们在子类继承父类之后,重写了父类的方法时,须要注意,这种做法并不好,减少了整个继承体系的复用性。出错几率会对应的添加。


总结前人的诸多经验来看,里氏替换原则主要是有四点:

1. 子类不要覆盖父类的非抽象的方法。

能够实现其抽象方法。

2. 子类能够实现自己独有的方法。

3. 子类的方法重写父类方法的时候。參数部分,要比父类的參数范围要大或者等于(宽松)。释义:举个样例>假设说父类的方法中形參是ArrayList,那么,其子类重写这种方法的时候,形參要是List.

4. 子类重写父类方法的时候,返回值要求。父类的返回值要比子类的返回值要小于或者等于。



面对这种情况,一般的,将当前的继承结构解除掉。变为依赖或者聚合组合的形式。

抽象出更高一层的抽象类,定义好这种一个抽象方法。同一时候由原先的两个类继承实现。


技术分享

public abstract class Calculator {

	public abstract void calc(int a, int b);
}

public class Calc extends Calculator{

	public void calc(int a, int b) {
		// a-b = ?
		System.out.println(a + " - " + b + " = " + (a - b));
		
	}
}

public class CalcSon extends Calculator{

	public void calc(int a, int b) {
		// a+b = ?
		System.out.println(a + " + " + b + " = " + (a + b));
	}
	
	// other method
	public void addThem(int a, int b) {
		System.out.println(a + b);
	}
}

通过这种途径将原来的继承结构又一次解构重组后的继承体系,应该说相对来说。出错的几率大大减少了。

源代码已经上传至GitHub:下载设计模式代码




以上是关于"围观"设计模式--里氏替换原则(LSP,Liskov Substitution Principle)的主要内容,如果未能解决你的问题,请参考以下文章

"围观"设计模式(15)--结构型之桥梁模式(Bridge Pattern)

"围观"设计模式(10)--创建型之原型模式(Prototype Pattern)

"围观"设计模式(14)--结构型之外观模式(Facade Pattern)

"围观"设计模式(11)--结构型之适配器模式(Adapter Pattern)

"围观"设计模式(17)--结构型之享元模式(Flyweight Pattern)

"围观"设计模式(22)--行为型之职责链模式(Chain Of Responsibility Pattern)