实现继承,语言缺陷
zhaorui在《When You Click…》回复:
“实现继承是某种程度上的语言缺陷” 这个我也不太理解,是否可以解释一下
好,那就解释一下。
面向对象的三个特征是什么?一个本科毕业生都可以对答如流:继承、封装和多态。在常规的理解中,继承分为实现继承和接口继承。简单说,实现继承就是为了把代码拿过来复用,而接口继承是给多态做准备。
面向对象和基于对象的差别就在于多态,程序是不是面向对象,看看有没有多态就知道了。我们还提倡面向接口编程,探究is-a关系,讲求各种各样的设计原则,讨论各种各样的设计模式。但仔细想一下就会发现,所有这些讨论都是围绕着多态,也就是接口继承的,跟实现继承没有任何关系。
那实现继承为什么还如此阴魂不散的围绕在我们身边呢?
最早大面积流行的面向对象程序设计语言是C++,所以,C++的选择影响了很多人,包括后来者Java和C#。所以,很多程序员一想到继承,就会想到把一些方法抽到基类里面,也有很多人愿意为了实现一个集合类,让自己的类继承自某个collection。其实,我们真正想要的不是继承,而只是要把一些公共操作放到一个地方,方便复用。
有了实现继承,随之而来会有很多问题,其中最著名的莫过于钻石问题。它的根源在于多重继承,绝大多数使用多重的场景只是为了从别人那里拿个实现过来。许多新语言都惟恐避之不及的躲开多重继承,事实证明,没多重继承,我们依然活着,还活得挺好。
如果你的世界里只有C++/Java/C#,那这是个让人头疼的问题,虽然有人会告诉你,少用继承,多用组合。但你也知道,写一大堆delegate,也挺讨厌的。说白了,语言不给力。
好吧!你知道我要说语言的事了。如果了解过Ruby,你应该知道module,如果你是个Scala程序员,trait是个利器。某种程度上说,C#的extension method也能做这件事。这些语言特性都能很好的解决真正的问题:代码复用。我们只要把需要复用的代码放到module/trait/extension method里,复用的问题就迎刃而解了,而无需小才大用的使用继承。
其实,如果语言可以解决复用的问题,那么,仔细想一下,我们会发现某些设计不甚合理的地方,比如Rails里面,Model为什么要从ActiveRecord::Base继承而来,我们只是需要某些能力而已,这样的话,也许把ActiveRecord::Base实现成一个module更为合理。
仔细了解一下语言的实现,我们不难发现,这些机制往往是在底层通过继承实现的。这从另外一个角度说明,或许实现继承本身就应该是一个底层实现,而非语言层面上的东西。
对于程序员来说,只会一门语言或一系列类似的语言,无疑于手里只有锤子,那时,眼里都是钉子,闭塞得很,可惜,这是大多数程序员的现状。