概述
声明:本文为阅读秦小波所写的《设计模式之禅》所写小结,文章内容可能有部分引述此书。
里氏替换原则(Liskov Substitution Principle)
1、定义:
里氏替换原则是针对于继承关系而言,只要父类能出现的地方,子类就可以使用,将子类完全替换为父类也不会出现任何异常,但反过来则不行,由子类出现的地方,父类并不一定能完全适应。总而言之:子类能替换父类,但父类并不一定能替换子类。
里氏替换原则为继承(extends)定义了一个良好的规范:
1. 子类必须完全实现父类方法;如果不行,则断开父子继承,采用依赖、聚集、组合等关系替代;
此处引例书中的一个说法:
士兵上阵杀敌,需要使用枪;枪的类型有手枪、步枪和机器,他们都有抠动扳机射击这个功能,因此抽象了一个AbstractGun类,定义了shoot方法。但现在突然来了一把玩具枪(ToyGun),玩具枪是不具备射击功能的,给到士兵也没法杀敌人,因此,不能将ToyGun归类到AbstractGun,而只能被定义为玩具:AbstractToy。AbstractToy可以对AbstractGun产生依赖,获取到AbstractGun的声音、外形等属性。
相关类图:
图1 枪支类图
2. 子类可以有自己的个性;但传参时不能向下转型(这是为啥父类不能替换子类),否则会出现类型转换错误;
延续上例:
关于步枪,其也有几种不同的型号,其中包括响亮的AK47和AUG狙击步枪;对于狙 击手来说,狙击步枪是其必备的,可以有瞄准镜,可以对敌人进行瞄准,给他一把普通的步枪,是没法进行瞄准的。其中狙击步枪是从步枪派生的,但是在给狙击手时,还是得给狙击步枪,而不是没有瞄准功能的普通步枪。
相关类图:
图2 Refle子类图
如果给Sinipper对象方法调用传参时:
snipper.setGun((AUG)(new Rifle()));
使用父类型强转,则会出现类型转换错误,导致向下转型的不安全。这里所能传入的,只能是子类型:
snipper.setGun(new AUG());
3. 覆盖或实现父类方法时,入参可以被放大(子类覆盖方法形参可以使用该参数的父类替代)。
4. 覆写或实现父类方法时,输出结果可以被缩小(子类覆写方法返回值可以是父类返回值的子类)。
此处要注意一点:
第三点和第四点的条件不一样,一个是覆盖,一个是覆写。
同名函数不同参数列表即为覆盖,覆盖又叫重载(Overload),子类调用时不会再调用父类同名方法;
子类实现了和父类相同的一个方法,就叫做覆写(Override),覆写又叫重写,子类调用时会最终调用到父类方法。
小结:采用里氏替换原则时,最好是尽量避免子类的“个性”。因为子类一旦有了“个性”,则子类出现的地方,父类就很难出现了。
注:本文大多数内容基本从《设计模式之禅中》摘录,也有小部分自己的理解,如果有误欢迎大家批评指正。如有问题,也欢迎大家积极留言探讨。
最后
以上就是瘦瘦野狼为你收集整理的设计模式之禅之里氏替换原则的全部内容,希望文章能够帮你解决设计模式之禅之里氏替换原则所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复