里氏替换原则
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;
}
}