目录
1、面向对象(oo)中继承的思考和说明
- 继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
- 继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障。
- 那么我们在编程当中,如何正确的使用继承呢? 答案是:遵循里氏替换原则
2、里氏替换原则基本介绍
- 里氏替换原则(Liskov Substitution Principle)在1988年,由麻省理工学院的一位姓里的女士提出的。指的是任何基类可以出现的地方,子类一定可以出现。
- 核心内容:继承必须确保超类所拥有的性质在子类中仍然成立,也就是说在继承时,子类中不要去重写父类中已实现的方法。
- 如果每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。
- 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
- 里氏替换原则告诉我们,继承实际上让两个类耦合性增加了,在适当的情况下,可以通过聚合,组合,依赖来解决问题。
3、应用实例
3.1、需求
- 电脑类:完成求和(a+b)的功能
- 笔记本电脑类:完成求和再求差(a+b-c)的功能
- 笔记本电脑类中可以使用电脑类中的求和方法
3.2、传统方式实现
3.2.1、类图
3.2.2、代码
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
Computer computer = new Computer();
System.out.println("1+2="+computer.calculate(1,2));
NotebookComputer notebookComputer = new NotebookComputer();
System.out.println("1+2-3="+notebookComputer.notebookCalculate(1,2,3));
}
}
/**
* 电脑
*/
class Computer{
public int calculate(int num1,int num2){
//求和
return num1 + num2;
}
}
/**
* 笔记本电脑
*/
class NotebookComputer extends Computer{
public int notebookCalculate(int num1,int num2,int num3){
//先求和 在求差
return calculate(num1,num2) - num3;
}
}
运行结果:
3.2.3、传统方式分析
上面的代码设计符合基本需求,但是:如果程序员在NotebookComputer(笔记本电脑类)中将父类Computer(电脑类)中的求和方法calculate无意间重写了(这是程序员无意识的,刚好写了一个与父类的caculate方法一样的方法),会发生什么呢?
看代码:其他的不变,只更改了NotebookComputer(笔记本电脑类)
/**
* 笔记本电脑
*/
class NotebookComputer extends Computer{
//程序员无意间重写了求和方法,使其实现的功能发生了改变
@Override
public int calculate(int num1, int num2) {
//父类的calculate是作加法,这里是作减法
return num1 - num2;
}
public int notebookCalculate(int num1, int num2, int num3){
//先求和 在求差
return calculate(num1,num2) - num3;
}
}
运行结果:
可见,这种继承是有很大几率出现问题的。当然,会出现的问题不只是上面的一种。
里氏替换原则的出现,就是为了解决诸如此类的问题。
3.3、遵循里氏替换原则实现
3.3.1、设计思路
- 给电脑(Computer)和笔记本电脑(NotebookComputer)找一个公共的父类:电子产品(Electronics);
- 电子产品类(Electronics)中可以写电脑和笔记本共有的特性;
- 电脑和笔记本电脑之间的关系不再是继承,而是依赖(笔记本电脑中会用到电脑)
3.3.2、类图
3.3.3、代码
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
Computer computer = new Computer();
System.out.println("1+2="+computer.calculate(1,2));
NotebookComputer notebookComputer = new NotebookComputer();
System.out.println("1+2-3="+notebookComputer.calculate(1,2,3));
}
}
/**
* 电子产品类
*/
class Electronics{
//可以写电脑与笔记本电脑共有的特征
}
/**
* 电脑类
*/
class Computer extends Electronics{
/**
* 加法
*/
public int calculate(int num1 ,int num2){
return num1 + num2;
}
}
/**
* 笔记本电脑类
*/
class NotebookComputer extends Electronics{
//依赖Computer类
private Computer computer = new Computer();
/**
* 先加法 后减法
*/
public int calculate(int num1 ,int num2,int num3){
return computer.calculate(num1,num2) - num3;
}
}
运行结果:
3.3.4、里氏替换原则总结
遵循里氏替换原则设计类的继承体系,可以避免前面所说的诸多问题。
里氏替换原则,简单用一句话来说就是:继承不重写、重载,父类出现的地方子类一定能出现,尽可能变继承为依赖。
本篇为我个人的理解思考与总结,有误之处请指出,谢谢 ^_^。