java设计模式—里氏置换原则(LSP)

一、定义

里氏置换原则(Liskov Substitution Principle),简称LSP

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

所有引用基类的地方必须能够透明的使用其子类对象。

所有引用基类的地方必须能透明地使用其子类的对象,通俗的来讲就是父类能出现的地方子类就可以出现,但是反过来就不行了。子类可以扩展父类的功能,但不能改变父类原有的功能。只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。

里氏替换原则为良好的继承定义了一个规范;

在类中调用其他类时务必要使用父类或者接口,如果不能使用父类或者接口,则说明类的设计已经违背了LSP原则;

我们在做系统设计时,经常会定义一个接口或者抽象类,然后编码实现,调用类则直接传入接口或者抽象类,不关心具体实现;

如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承;

在项目中采用里氏替换原则时,尽量避免子类的个性,一旦子类有个性这个子类和父类的关系就很难调和了。

定义包含四层意思:

1、子类可以实现父的抽象方法,但不能覆写父类的非抽象方法。 父类中凡是已经实现好的方法(相对于抽象方法而言),实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。

如下示例:

定义一个抽象类:

package com.dp.lsp;

/**
 * 定义一个图形抽象类
 * @author lijbe
 *
 */
public abstract class Figure {

	private String figureName;
	

	
	public void setFigureName(String figureName){
		this.figureName = figureName;
	}

	public String getFigureName() {
		return figureName;
	}

	/**
	 * 打印图形名称
	 */
	public abstract void printFigureName();
}

定义两个实现类:

package com.dp.lsp;

public class Rectangle extends Figure{

	@Override
	public void printFigureName() {
		this.setFigureName("我是长方形");
		System.out.println(this.getFigureName());
	}

	
	
}

圆形:

package com.dp.lsp;

public class Roundness extends Figure {

	@Override
	public void printFigureName() {
		this.setFigureName("我是圆形");
		
		System.out.println(this.getFigureName());

	}

}

定义一个调用类:

package com.dp.lsp;

public class ClientLsp {

	
	public static void printFigureName(Figure figure){
		
		figure.printFigureName();
	}
	
	public static void main(String[] args) {
		Figure rectangle = new Rectangle();
		ClientLsp.printFigureName(rectangle);
		
		
		Figure roundness = new Roundness();
		ClientLsp.printFigureName(roundness);
	}
}

输出结果:

我是长方形
我是圆形

如果我们把父类的setFigureName方法在Roundness类中重写了,再试一下。

package com.dp.lsp;

public class Roundness extends Figure {

	
	@Override
	public void printFigureName() {
		this.setFigureName("我是圆形");
		
		System.out.println(this.getFigureName());

	}

	@Override
	public void setFigureName(String figureName){
		// do nothing
	}
}

输出结果:

我是长方形
null

这就破坏了原有的继承规则,不符合LSP原则。

2、 子类中可以增加自己特有的方法。

package com.dp.lsp;

public class Roundness extends Figure {

	
	
	@Override
	public void printFigureName() {
		this.setFigureName("我是圆形");
		
		System.out.println(this.getFigureName());

	}

	@Override
	public void setFigureName(String figureName){
		// do nothing
	}
	
	public double calculateArea(double radius){
		double pi = 3.14;
		
		return pi*radius*radius;
	}
}

3) 覆写或实现父类的方法时,输入参数可以被放大。(覆写指的覆写一个正常方法并重写,实现指的是实现接口或者抽象方法)

4) 覆写或实现父类的方法时输出结果可以被缩小(若放大,还能用子类替换父类吗?)


总之,LSP原则核心就是子类可以替换父类,而父类不可以替换子类。因为子类包含了父类的所有特性,而父类则不会包含子类的所有特性,因为子类有自己扩展的特性。另外一层原则是:子类不要轻易的改变(覆写)父类实现的方法,因为这也会破坏继承规则。





你可能感兴趣的:(设计模式)