为什么把他俩一起说呢 因为我觉得他们俩是息息相关的 联系比较高 虽然不像 依赖倒转原则和开闭原则那样紧密 接下来看我娓娓道来
八股文(概念)
里氏替换原则
捞干的来:如果一个子类可以被任何父类所使用,那么这个子类就满足里氏替换原则
通俗一点的说 在代码中任何引用这个父类的地方 如果把父类换成他的子类 那么代码也可以正常运行
那么就必须保证 子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
合成复用原则
讲这个之前 先说一下继承存在的问题(当然继承也有它的优点 先不谈):
- 继承复用破坏了类的封装 因为子类继承会将父类的细节暴露给子类 子类对子类是透明的 简称白箱复用
- 耦合度高 如果修改父类 那么子类的实现也会发生变化 不利于拓展维护
- 灵活性低 从父类继承来的是静态(在编译时已经定义)的,所以运行时不可能发生变化
合成复用的概念:将已有的对象纳入到新对象中,新对象可以调用已有对象的功能
合成复用可以很好的解决以上的问题
- 保证了封装性 成分对象的内部细节新对象看不见 也被成为黑箱
- 耦合度低 可以在类的成员位置声明抽象
- 灵活性高 这种复用是在运行时动态进行 新对象动态引用成分对象类型相同的对象
所以说我才觉得这两个有一定的联系 里氏替换原则表示子类继承父类时不要重写父类里面的内容,所以说继承这一特性还是存在一定问题 所以尽量使用组合或聚合的方式 (统称合成复用)不是使用继承
代码
使用继承并不遵守里氏替换原则的反例:
下面的几维鸟类由于重写了父类的方法 导致除数为0 正确的方案应该进行重构聚合或从新划分接口(感觉概念性较强 不做演示)
public class LSPtest {
public static void main(String[] args) {
Bird bird1 = new Swallow();
Bird bird2 = new BrownKiwi();
bird1.setSpeed(120);
bird2.setSpeed(120);
System.out.println("如果飞行300公里:");
try {
/**
* 如果飞行300公里:
* 燕子将飞行2.5小时.
* 几维鸟将飞行Infinity小时。
*/
System.out.println("燕子将飞行" + bird1.getFlyTime(300) + "小时.");
System.out.println("几维鸟将飞行" + bird2.getFlyTime(300) + "小时。");
} catch (Exception err) {
System.out.println("发生错误了!");
}
}
}
//鸟类
class Bird {
//速度
double flySpeed;
public void setSpeed(double speed) {
flySpeed = speed;
}
//计算飞行时间
public double getFlyTime(double distance) {
return (distance / flySpeed);
}
}
//燕子类
class Swallow extends Bird {
}
//几维鸟类
class BrownKiwi extends Bird {
@Override
public void setSpeed(double speed) {
flySpeed = 0;
}
}