为耦合辩护,为继承伸冤
为耦合辩护,为继承伸冤
大家都听说过,有个称号叫“基于对象”,用来授予那些
没有资格获得“面向对象”称号而又想往上靠的那些语言,
而不够资格据我所知都是因为不支持继承,可以说是否支持
继承是“面向对象”和“基于对象”之间的分水岭.
继承如此重要,自从他和“面向对象”一起出世后就很
受重用.可一直有一股暗流在地下涌动,最近甚至跳到了明处.
他们公开指责继承和耦合之间关系密切,并通过诬蔑耦合为
万恶之源的办法来达到诽谤继承的目的,并且公开叫嚣要少
用继承甚至不用继承.
事情是否真的象他们说的那样吗?难道模块独立性真的
那样的珍贵,低的耦合度真的那样的美好,竟值得用放弃继承
和使用“基于对象”语言为代价吗?
先谈耦合,一般来说,耦合不是什么好东西,耦合度越低
越好.但这并不是绝对的.
首先,不耦合和低耦合在很多情况下不太可能被实现.
不耦合是从理论上就是根本不可能的,只有数据耦合在实际
上也是不可能的,不然怎么会有什么类,还不是因为不管怎
么搞,函数们仍然一伙一伙的通过数据结构紧紧的耦合在一
起.至于类之间比较深的耦合也不只是继承,比如:Iterator.
不使用深耦合很多问题不可能解决.
而且,耦合最深的不是函数之间,更不是类之间,而是函
数内部,这个耦合问题是永远也不可能解决的.
其次,低耦合并不很重要.耦合度的重要性要小于内聚度,
在不能同时保证时,应先保证高内聚而不是低耦合.
再次,低耦合也未必有多大好处.低耦合往往造成模块的
数量成倍的增加或是出现一些过于巨大的模块.模块过多,使
用者了解单个模块简单了,但要了解更多的模块,可能把好处
抵消得差不多了.模块过大则更可怕了,以为过大的模块接口
可能是低耦合的,可内部肯定耦合的一塌糊涂.
再次,高耦合也常常有好处.在不少情况下,高耦合度的模
块要比低耦合度模块效率高.有时,做一个高耦合度模块是替代
复制和粘贴的最好办法.
最后,高耦合带来的害处是可以化解的.化解的办法有两个:
熟悉和标准化.
熟悉的作用,举个简单的例子,DOS,UNIX,Windows的系统
调用,那个不是涉及到系统内部的数据结构和实现细节,这可
不是一般的耦合了.可在这些系统上工作的程序员们不都活
得好好的吗?其实熟悉了也就不太用在乎高耦合了.
标准化,作用就更大了.我们可以为模块订出标准,耦合
度达到什么程度,什么细节需要暴露,什么细节必须隐藏,我
们都订成标准,一个版本一个版本的支持下去.上个例子中的
操作系统不都是这么干的吗?
另外,熟悉的人多的东西容易成为标准,成为标准的东西
容易被人熟悉.
现在谈继承和耦合之间的关系.主要是比较继承-耦合关
系和聚合-耦合关系.可能有不少人反复的告诉你们继承和耦
合关系十分密切,而聚合和耦合之间则关系疏远.真有这么回
事吗?
1.其实你现在看到的高耦合的继承和低耦合的聚合是一
种错觉,因为继承和聚合都出现在一个系统里,自然把高耦合
的部分做成继承,低耦合的部分做成耦合.如果你全用继承,
你将得到高耦合的继承和低耦合的继承;如果你全用聚合,你
将得到高耦合的聚合和低耦合的聚合.尤其可怕的是高耦合的
聚合,因为你不得不把某些类的细节完全公开,结果你会发现
聚合破坏起封装性来比倍受攻击的继承拿手多了.
2.聚合和相识关系有时也需要高耦合,例子就是Iterator.
3.可以建立独立于类实现的继承接口来降低继承的耦合
度,就象JAVA中的Interface独立于类实现一样.
综上所述,耦合并不象某些人所说的那样可怕,继承也
并不一定会带来比别的方式更高的耦合度.
关于继承和耦合这次就先说到这里,下次再说.
梁海鹰 2001年8月7日于北京