LSP。。。咳咳。。。你懂的。。。

好吧,并不是你想的那样。。。LSP的另一种翻译叫做里氏替换原则。原来的定义太过复杂根本看不懂也没必要搞懂了,通俗意义上就是说子类的指针或者引用可以随时被替换成父类的指针或者引用,这样多态的行为仍保持不变,不管你是什么引用,具体调用结果只看被引用的对象是哪个。

这就等价于子类必须拥有父类的全部特征,包括所有的方法和变量。

听起来好像平平无奇,现在的编程语言的继承就是按照这个原则设计的,根本不需要码农去做什么,一个继承一写,就自然满足了这个LSP。。咳咳。。。

但是这里面有个漏洞。

举个例子,有一个基类叫做马,马会跳,所以这个基类就有一个方法叫jump,jump的实现是返回马的身高h的2倍。

好了,你写了10个类继承了马这个类,代表了10种不同的马,这没有问题。

不过你们系统里还有最后一种马,不妨叫做健马,健马因为太健了所以不会跳。

现在问题来了,这个健马的类要不要继承马这个类呢,如果不继承,那么除了跳以外的基类马的其他99个姿势其实健马也会的,不能因为一个跳而放弃了其他99种姿势你说对不对,所以还是硬着头皮让健马继承了马。

好,那么一继承,基类马中的jump方法在健马中该怎么处理呢?

那么一种大聪明们喜欢用的做法就是override这个jump method,在健马的实现中直接抛一个NotImplementedException。

个么个记就豁边了。这么一搞就直接违反了LSP原则。因为此时用基类的引用去调用其他马的jump和调用健马的jump会让调用者惊掉下巴,健马的jump居然会抛exception,偶又不知道健马会抛异常的啦,偶调用jump的外面没有加try catch。。。

试想一下如果这个基类的引用是通过某个库返回给调用方的,调用方岂不是两眼墨测黑,天知道那个库里面创建的是健马还是什么别的马。。。

好了,既然健马的jump里面不能抛异常,个么我实现为一个空方法可以伐啦,什么都没有的空方法总可以咯了。

可是别忘了基类原来的实现是返回马身高的2倍,是有返回值的,敢问哝空方法如何返回,返回啥个值呢?总不能返回0吧?万一调用方下一行代码是用猪的jump高度除以健马的jump高度。。。

返回任何值都是错误的。。。当然你可以说我直接返回健马的身高,但我说有些马jump起来其实会把身体压扁而不是跳高,导致它们的jump返回值小于身高。。。那么你这个hack就不成立了。

不会跳就是不会跳,那还不如就让这个jump在健马上根本不存在,这样外界根本调不了这样最好。

代码的问题都可以通过引入新的层次来解决。

我引入一个新的基类叫做不会跳的马,然后会跳的马这个基类继承自不会跳的马这个基类,然后所以别的马继承会跳的马这个基类,健马继承不会跳的马这个基类,问题是不是就解决了。

但这样增加了一个继承层级,这让这个系统更加的不灵活。另一种方法是所有会跳的马都实现一个jump的interface,马这个基类里我就不加jump了,这样更优美些。interface就是在这时候用的。不过如果特殊情况很多,子类要实现很多个interface,那这种方法也不经济,总之这两种需要权衡。

上一篇 下一篇