里氏替换原则
【 概念 】
对于继承的思考
- 继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契
约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。- 使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性。
- 如果一个类被其他的类所继承则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障
- 问题提出:在编程中,如何正确的使用继承?=>里氏替换原则
【 特点 】
凡是基类出现的地方 都能使用子类进行替换
- 所有引用基类的地方 必须能透明地使用其子类的对象
- 使用继承时 子类尽量不要重写父类的方法
- 继承实际让两个类耦合增强,适当情况可以通过聚合、组合、依赖来解决问题
【 场景介绍 】
类B在继承类A时可能 无意识重写了类A的方法导致错误(如函数重名等)
【 违反–里氏替换原则 】
如图:类B在继承类A时 可能会重写修改类A的所有方法(造成侵入 可能破坏功能)
public class LisKov {
public static void main(String[] args) {
A a = new A();
System.out.println("11 - 3 = "+ a.func1(11,3));
System.out.println("-----------");
B b = new B();
System.out.println("11 - 3 =" + b.func1(11,3));
System.out.println("11 + 3 + 9" + b.func2(11,3));
}
}
class A {
//返回两个数的差
public int func1(int num1, int num2){
return num1 - num2;
}
}
//此时B类继承了A
//增加一个新功能:完成两个数相加,然后和9相加
class B extends A{
//在继承时无意识完成了 重写
public int func1(int num1, int num2){
return num1 + num2;
}
public int func2(int num1, int num2){
return func1(num1, num2) + 9;
}
}
【 运行结果 】
可以看到类A的fun1任然按照类A定义的步骤执行
但类B因为无意中重写了父类的方法,造成原有功能出现错误。
在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候
【 遵循–里氏替换原则 】
如果一定要产生继承关系,可以将B类提升至于A类同级 并共同继承基类
如果B类一定要使用A类的方法 需要将A类与B类进行组合
public class LisKov {
public static void main(String[] args) {
A a = new A();
System.out.println("11 - 3 = "+ a.func1(11,3));
System.out.println("-----------");
B b = new B();
//此处为B类实现的自己专属功能
System.out.println("11 + 3 = " + b.func1(11,3));
System.out.println("11 + 3 + 9 = " + b.func2(11,3));
//通过B类调用A类重写的方法
System.out.println("11 - 3 = " + b.func3(11,3));
}
}
//抽离出公共基础类
class Base{
public int func1(int a,int b){
return a - b;
}
}
//类A继承基础类 重写func1方法完成自己的功能
class A extends Base{
//返回两个数的差
public int func1(int num1, int num2){
return num1 - num2;
}
}
//此时B类继承Base 又一次重写func1方法 修改为自己的功能
class B extends A{
//使用组合方式 调用A对象方法
private A a = new A();
//B类在这里 重写为自己的功能
public int func1(int num1, int num2){
return num1 + num2;
}
public int func2(int num1, int num2){
return func1(num1, num2) + 9;
}
public int func3(int num1, int num2){
return this.a.func1(num1, num2);
}
}
【 运行结果 】
类Bfunc1实现了自己的功能 同时也能正确使用类A的fun1功能
PS:笔者认为上述里氏替换原则使用面向接口编程手段会更灵活