• 2011-08-15

    实现继承,语言缺陷

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/156324613.html

    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更为合理。

    仔细了解一下语言的实现,我们不难发现,这些机制往往是在底层通过继承实现的。这从另外一个角度说明,或许实现继承本身就应该是一个底层实现,而非语言层面上的东西。

    对于程序员来说,只会一门语言或一系列类似的语言,无疑于手里只有锤子,那时,眼里都是钉子,闭塞得很,可惜,这是大多数程序员的现状。

    分享到:
    引用地址:

    评论

  • 完全抛弃”实现继承“,转而使用”接口继承“。恐怕会陷入另一个怪圈。。。问题就出现在你这样划分继承类别,是建立在对象是由属性和方法构成的这样一个基本的事实(属性对应实现继承,方法对应接口继承),而如果你的假设成立,那也就是等于说对象根本就不需要属性,恐怕这样的抽象很难让人接受吧。。。
    回复KILLVIN_LIU说:
    这个?貌似我没有说属性对应实现继承,方法对应接口继承这样的话吧!
    2011-08-20 13:36:05
  • 基本同意郑大校长的意见。我也在尝试通过Ruby或者其他语言来看待OO的本质。但我对于实现继承而言,仍然是有保留的接受。锤子本身没有什么问题,其实是用锤子的人有问题。实现继承最有利是复用共性的内容。这个观点我上次也提过了。呵呵:)
  • 我觉得dreamhead大大这里误解了c++,c++ code standards里明文提出,继承是为了复用子类,而非复用父类。c++里也是有利器的:boost::bind / boost::function.可以任意的把想要的函数绑定到想要的对象里面,完全无视继承。
    回复hurricane1026说:
    现实是,很多人用继承是为了复用代码,也就是父类,这是非常不怎么样的想法,虽然很多人在说,这种做法不好。如果说我误解,不如说你不了解很多人编码的底线,低得让人害怕。
    2011-08-16 22:00:05