设计模式:5.设计模式的七大原则之四(里氏替换原则)

101 阅读3分钟

里氏替换原则

1.OO中的继承

继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。

2.继承的弊端

使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性。如果一个类被其他类所继承,则当这个类需要修改时,必须考虑到所有的子类。
在编程中使用继承,需要遵循 里氏替换原则

3.里氏替换原则

所有引用基类的地方必须能透明的使用其子类的对象,即在子类中尽量不要重写父类的方法。可以通过聚合、组合、依赖来解决问题。

4.实际案例
  • 有问题的案例:
/** 父类A **/
public class A {
	// 方法func1:返回传入两数差
	public int func1(int num1, int num2){
		return num1 - num2;
	}
}
/** B类继承A **/
public class B extends A{
	// B类中方法fun1:返回两数之和,无意间重写了父类的方法
	public int func1(int a, int b){
		return a + b;
	}
	
	// 方法func2:返回两数之和再加9
	public int func2(int a, int b){
		return func1(a,b) + 9;
	}
}

/** 调用 **/
main(String[] args){
	A a = new A();
	a.func1(10, 5);	// 想计算10-5,结果为5

	B b = new B();
	b.func1(10, 5);	// 这里本地是使用父类的已经实现的方法实现10-5,但是func1已经被无意间重写了,结果为15,错误
	b.func2(10, 5);	// 结果为24
}
  • 解决方法:
    上面的案例,类B无意将重写了父类A的方法,造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。
    通用的解决方法:原来的父类和子类都集成一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合、组合等关系代替。
/** 基类 **/
class Base {
	// 更通俗的方法写在这里
}

/** 类A **/
class A extends Base {
	// 方法func1:返回传入两数差
	public int func1(int num1, int num2){
		return num1 - num2;
	}
}

/** 类B **/
class B extends Base {
	private A a = new A();
	
	// B类中方法fun1:返回两数之和,因为现在B并不是继承A,所以此处并不存在重写关系
	public int func1(int a, int b){
		return a + b;
	}
	
	// 方法func2:返回两数之和再加9
	public int func2(int a, int b){
		return func1(a,b) + 9;
	}

	// 新增fun3:返回两数之差再加9   这里我们发现A类中有已经实现的返回两数之差方法,想用A
	// 则新增一个成员变量a,通过实例化的A类对象调用即可,这也就是通过A类与B类的组合实现功能
	public int func3(int num1, int num2){
		return a.func1(num1, num2) + 9;
	}
}