里氏替换原则
子类型必须能够替换掉它们的父类型。
也正因为有了这个概念,才让开放-封闭原则成为可能,因为开放必须依赖继承。想弄明白里式替换,就必须理解面向对象编程中的多态这个概念,只有子类可以替换掉父类,且软件功能不受影响时,父类才能真正的被复用,而子类也能够在父类的基础上增加自己的行为。举个例子:
动物都有叫这种行为(不考虑特例不会叫的动物),猫继承了动物,自然有自己的独特叫声。
Animal animal = new Cat();
animal.叫();
当需求变化时,只要把猫替换成其他动物,其余部分不变,即:
Animal animal = new Dog();
animal.叫();
这里Cat和Dog都继承了Animal类。
子类型的可替换性使得父类型模块在无需修改的情况下就能扩展。这里同时引出另一个原则,依赖倒转原则。
依赖倒转原则
A、高级模块不应该依赖低级模块,两者都应该依赖抽象。
B、抽象不应该依赖细节,细节应该依赖抽象。
单纯的概念不容易理解,我们直接举栗子。上面我们说了动物类下有猫和狗,这时候我们引入一个动物寄养中心类,每天需要让动物发出叫声进行打卡。
开始的时候,我们只寄样的猫。突然一天,我们把猫取走了,寄存了一直狗,如果想让狗每天叫来打卡,则需要修改代码才能完成需求,这样就违反了开闭原则。
public class AnimalService {
private Cat cat;
public AnimalService(Cat cat) {
this.animal = cat;
}
public void 叫(){
if (cat != null) {
cat.叫();
}
}
}
这时候就可以用依赖倒转+里式替换原则来优化我们的代码。
/**
* Create by songzi on 2019/12/15
*/
public class AnimalService {
private Animal animal;
public AnimalService(Animal animal) {
this.animal = animal;
}
public void 叫(){
if (animal != null) {
animal.叫();
}
}
}
public class Dog extends Animal {
@Override
public void 叫() {
System.out.println("汪汪汪");
}
}
public class Cat extends Animal{
@Override
public void 叫() {
System.out.println("喵喵喵");
}
}
这里AnimalService不需要关系具体存放在这里的是什么动物,只是定时让动物叫即可。不管以后存放的是牛还是羊,只需要继承动物类并且完成自己的叫方法,传递进来即可。即高层AnimalService不依赖底层Cat,而是依赖抽象Animal。