里氏代换原则
里氏代换原则通俗的解释就是任何可以出现基类(父类)的地方必须能够透明地使用子类对象。里氏代换原则是实现开闭原则的基础,在程序设计中应该尽量使用基类来对对象进行定义,在运行时在确定要使用的子类,用子类对象来替换父类对象,里氏代换原则强调面向父类编程。
但是里氏代换原则需要注意的几点有:
1、子类中的所有方法必须在父类中声明,或者说子类必须是吸纳父类的全部方法
为什么这么说呢?比如有下面这样两个类:
我们可以看到子类SubClass继承了基类BaseClass并实现了Operation1()方法,同时又增加了Operation2()方法,如果我们用SubClass obj=new SubClass();那么我们的这个obj对象是访问不到Operation2()方法的,也就是当我们针对父类编程则无法使用子类中新增的方法Operation2(),要想使用Operation2()方法不能用父类声,必须使用子类进行声明,说明这样的设计思路是违反里氏代换原则的。
2、在运用里氏代换原则的时候要尽量把类设计成接口或者抽象类,让子类去继承父类或者实现接口,实现父类中声明的方法,在运行时使用子类替换父类实例,我们可以很方便的来扩展系统。
---------------------------------------------------低调的分割线----------------------------------------
我们使用一个例子来说明这个具体的问题,动物管理员要喂猴子水果,当然每天的喂的水果可能是苹果也可能是香蕉。那么我们这个设计会这样设计:
代码:
public class ZooKeeper {
public static void main(void String []args) {
// TODO implement here
Apple apple=new Apple();
Monkey monkey=new Monkey();
monkey.setApple(apple);
monkey.eat();
}
}
与之对应的的Monkey中的方法可能存在下面的代码:
public void eat() {
apple.eat();
}
如果动物管理员要给猴子喂香蕉吃,那么我们就必须修改猴子eat()中的源代码源代码。那么现在我们可以使用里氏代换原则对这个设计进行重构。
客户类ZooKeeper可以通过读取配置文件config.xml文件通过反射生成对象注入到Monkey中。根据里氏代换原则这里setFruit可以接受Apple的子类Banana的对象,从而大大提高程序的灵活性。
面向对象更好的实现了复用,而要实现高复用性就要遵从开闭原则(对修改组件的代码关闭,对扩展组件开放),而里氏代换原则是实现开闭原则的基础。