全部博文(465)
分类: IT业界
2011-08-15 16:41:37
在老子的“小国寡民”论中,提出了一种理想的社会状态:邻国相望,鸡犬之声相闻,民至老死,不相往来。这是他老人家的一种社会理想,老死不相往来的人群呈现了一片和谐景象。因为不发生瓜葛,也就无所谓关联,进而无法导致冲突。这是先祖哲学中的至纯哲理,但理想的大同总是和现实的生态有着或多或少的差距,人类社会无法避免联系的发生,所以小国寡民的理想成为一种美丽的梦想,不可实现。同样的道理,映射到软件“社会”中,也就是软件系统结构中,也预示着不同的层次、模块、类型之间也必然存在着或多或少的联系,这种联系不可避免但可管理。正如人类社会虽然无法实现小国寡民,但是理想的状态下我们推崇和谐社会,把人群的联系由复杂变为简单,由曲折变为统一,同样可以使得这种关联很和谐。所以,软件系统的使命也应该朝着和谐社会的目标前进,对于不同的关系处理,使用一套行之有效的哲学,把复杂问题简单化,把僵化问题柔性化,这种哲学或者说方法,在我看来就是:依赖的哲学,也就是本文所要阐释的中心思想。
因为“耦合是不可避免的”,所以首先就从认识依赖和耦合的概念开始,来一步步阐释依赖的哲学思想。
(1)什么是依赖和耦合
依赖,就是关系,代表了软件实体之间的联系。软件的实体可能是模块,可能是层次,也可能是具体的类型,不同的实体直接发生依赖,也就意味着发生了耦合。所以,依赖和耦合在我看来是对一个问题的两种表达,依赖阐释了耦合本质,而耦合量化了依赖程度。因此,对于关系的描述方式,就可以从两个方面的观点来分析。
从依赖的角度而言,可以分类为:
· 无依赖,代表没有发生任何联系,所以二者相互独立,互不影响,没有耦合关系。
· 单向依赖,关系双方的依赖是单向的,代表了影响的方向也是单向的,其中一个实体发生改变,会对另外的实体产生影响,反之则不然,耦合度不高。
· 双向依赖,关系双方的依赖是相互的,影响也是相互的,耦合度较高。
从耦合的角度而言,可以分类为(此处回归到具体的代码级耦合概念,以方便概念的阐释):
· 零耦合,表示两个类没有依赖。
· 具体耦合,如果一个类持有另一个具体类的引用,那么这两个类就发生了具体耦合关系。所以,具体耦合发生在具体类之间的依赖,因此具体类的变更将引起对其关联类的影响。
· 抽象耦合,发生在具体类和抽象类的依赖,其最大的作用就是通过对抽象的依赖,应用面向对象的多态机制,实现了灵活的扩展性和稳定性。
不同的耦合,代表了依赖程度的差别,以“粒度”为概念来分析其耦合的程度。引用中间层来分离耦合,可以使设计更加优雅,架构更加富有柔性,但直接的依赖也存在其市场,过度的设计也并非可取之道。因为,效率与性能同样是设计需要考量的因素,过多的不必要分离会增加调用的次数,造成效率浪费。
后文分析依赖倒置原则的弊端之一正是对此问题的进一步阐述。
(2)耦合是如何产生的
那么,软件实体之间的耦合是如何产生呢?回归每天挥洒的代码片段,其实就是在重复的创造着耦合,并且得益于对这种耦合带来的数据通信。如果将历史的目光回归到软件设计之初,人类以简单的机器语言来实现最简单的逻辑,给一个输入,实现一个输出,可以表达为如图3-1所示的形式。
随着软件世界的革命,业务逻辑的复杂,以上的简单化处理已经不足以实现更复杂的软件产品,当系统内部的复杂度超越人脑可识别的程度时,就需要通过更科学的方法或者方式来梳理,如图3-2所示。
因此,人类开始发挥重组和简单化处理的优势,开发者不得不在软件设计上做出平衡。平衡的结果就是通过对复杂的系统模块化,把复杂问题简单处理,从而达到能够被人脑识别的目的。基于这种指导原则,随着复杂度的增加模块的划分更加朝着精细化发展,尤其是面向对象程序设计理论的出现,使得对复杂的处理实现了更科学的理论基础。然而,复杂的问题可以通过划分实现简单的功能模块或者技术单元,但由此应运而生的子单元会越来越多,而且越来越多的子单元必须发生数据的通信才能完成统一的业务处理,所以产生的数据通信管理也越来越多。对于子单元的管理,也就是本文关注的核心概念——依赖,成为新的软件设计问题,那么总结前人的经验,提炼今人的智慧,对耦合的产生做如下归纳:
· 继承
· 聚合
· 接口
· 方法调用和引用
· 服务调用
了解了耦合发生的一般方式,就可以进入核心思想的讨论,那就是在认识和了解依赖的基础上,最终追求的目标。
话说 |
设计的目标:高内聚(High cohesion)、低耦合(Low coupling)。 |
讨论了半天,终于是时候对依赖和耦合进行一点儿总结了,也是该进行一点目标诉求了。在软件设计领域,有那么几个至高原则值得每个开发者铭记于心,它们是:
· 面向抽象编程
· 低耦合,高内聚
· 封装变化
· 实现重用:代码重用、算法重用
对了,就是这些平凡的字眼,汇集了面向对象思想的核心内容,也是本文力求阐释的禅意心经。关于面向抽象编程和封装变化,会在后面详细阐释,在此我们需要将注意力关注于“低耦合,高内聚”这一目标。
低耦合,代表了实现最简单的依赖关系,尽可能地减少类与类、模块与模块、层次与层次、系统与系统之间的联系。低耦合,体现了人类追求简单操作的理想状态,按照软件开发的基本实现技巧来追求软件实体之间的关系简单化,正是大部分设计模式力图追求的目标;低耦合,降低了一个类或一个模块发生修改对其他类或模块造成的影响,将影响范围简单化。在本文阐释的依赖关系方式中,实现单向的依赖,实现抽象的耦合,都是实现低耦合的基础条件。
高内聚,一方面代表了职责的统一管理,一方面体现了关系的有效隔离。例如单一职责原则其实归根结底是对功能性的一种指导性体现,将功能紧密联系的职责封装为一个类(或模块),而判断的准则正是基于引起类变化的原因。所以,封装离不开依赖,而抽象离不开变化,二者的概念和本质都是相对而言的。因此,高内聚的目标体现了以隔离为目标进行统一管理的思想。
那么,为了达到低耦合、高内聚的目标,通常意义上的设计原则和设计模式其实都是朝着这个方向实现的,因此仅仅总结并非普遍意义的规则:
· 尽可能实现单项依赖。
· 不需要进行数据交换的双方,不要实现多此一举的关联,人们将此形象称为“不要向陌生人说话(Don't talk to strangers)”。
· 保持内部的封装性,关联的双方不要深入实现细节进行通信,这是保证高内聚的必需条件。
本文节选自《你必须知道的.NET(第2版)》一书
图书详细信息:http://blog.chinaunix.net/space.php?uid=13164110&do=blog&id=2149571