编程模式---里氏代换原则(LSP)

     从“开-闭”原则中可以看出面向对象涉及的重要原则是创建抽象化,并且从抽象化导出具体化。具体化可以给出不同的版本,每一个版本都给出不同的实现。从抽象化到具体化的导出要使用继承关系和这里要引入的里氏代换原则。

一、什么是里氏代换原则

     里氏代换原则描述如下:如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都替换成o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型。换言之,一个软件实体如果使用的是一个基础类的话,那么一定适用于其子类,而且他根本不能觉察出基类对象和子类对象的区别。

    假设有两个类,一个是base类,另一个是extend类,并且extend类时base类的子类。那么一个方法如果可以接受一个基类b的话:method(base b);那么他必然可以接受一个子类对象e,也即可以有method(e).

     里氏代换原则是继承复用的基石。要有当衍生类可以替换掉基类,软件单位的功能不会受到影响时,基类才能正真被复用,而衍生类也才能够在基类的基础上增加新的行为。

注意:反过来的代换不成立

     必须要指出的是,反过来的代换则不成立,即如果一个软件实体适用的是一个子类的话,那么他不一定适用于基类。如果一个方法method接受子类对象为参数的话method(extend e),那么一般而言不可以有method(base b)。

二、java对里氏代换的支持

     里氏代换要求凡是基类型适用的地方,子类型一定适用,因此子类必须具备基类型的全部接口。或者说,子类型的接口必须包括全部的基类型的接口,而且还有可能更宽。如果一个java程序破坏了这一条件,java编译器就会给出编译时期的错误。在其他语言中这一特点也是适用的。

     例如,客户端要调用超类型的一个公开方法。如果以子类型代之,这个方法却变成了私有的,客户端不可能调用。显然这是违反了里氏代换原则的,java编译器根本不会让这样的程序过关。

三、java对里氏代换支持的局限

     java编译器的检查是有限的。例如:描述一个物体大小的量有精度和准度两个属性。所谓精度,就是这个量的有效数字有多少位;所谓准度,是这个量与真实的物体大小符合到什么程度。一个量可以有很高的精度,但是无法与真实的物体的情况相吻合。java语言编译器能够检查的,仅仅是相当于精度属性而已,它无法检查这个量与真实物体的差距。

     换言之,java编译器不能检查一个系统在实现和商业逻辑上是否满足里氏代换法则。一个著名的例子就是“正方形类是否是长方形的子类”的问题。

四、墨子论道

     ①《墨子·小取》中说“白马,马也;乘白马,乘马也。骊马,马也;乘骊马,乘马也。”所谓的骊马,便是黑色的马。墨子在这里说的是,不论黑马、白马均是马的一种。既然马可以骑,那么白马和黑马必可骑。这也就说出了里氏代换的精髓:父类适合的地方,那么用子类也一定适合。

     ②《墨子·小取》中说“娣,美人也,爱娣,非爱美人也... ...盗,人也,恶盗,非恶人也。”娣便是妹妹,哥哥爱妹妹,是因为两人的兄妹关系,而不是因为妹妹是个美人。因此,喜爱妹妹不代表喜爱美人。在面向对象的演员来说,美人是一个基类,妹妹是美人的子类。哥哥可以作为一个类有个“喜爱()”方法,接受妹妹作为参数。却不一定接受美人类的实例。

五、总结

     里氏代换原则讲的是基类和子类的关系。只有当这种关系存在时,里氏代换关系才存在;反之则不存在。有关其他的例子,大家可以搜索一下正方形为什么不能是长方形的子类,而需要引申出一个四边形。

 

 

本文通过总结《java与模式》而来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值