0.写在前面
什么是好的代码?好的代码应该模块化。
王垠在其《编程的智慧》中也提到,要“写模块化的代码”。(不对人做评价,这篇文章写得是非常好的。)
如果你读过《代码大全》和《代码整洁之道》等书,一定对“高内聚、低耦合”不陌生。
好的模块化代码就是要高内聚、低耦合。
事实上,内聚和耦合是年就提出的概念,由于耦合不好具体的衡量,MeilirPage-Jones在年提出了共生性(Connascence)。本章重点就是介绍如何评估模块化架构,以及引入共生性这一概念来帮助更好的模块化。
1.模块化
不同的平台、语言为代码提供了不同的复用机制,将相关代码组合成模块。
理解模块对于架构师来说非常重要,因为用来分析架构的工具(可视化等)常常都依赖于模块化的概念。如果一个架构师在设计一个系统时,没有注意到各个部分是如何连接在一起的,那么他们最终创建的系统会带来无数的问题。
架构师必须保持良好的结构,这不会偶然发生。
模块的代码到底是什么?我们用模块化来描述相关代码中的逻辑分组,这些模块可以用来构造一个更复杂的结构。
现代的语言有各种各样的封装机制,例如,许多语言可以在函数/方法、类、包/命名空间中定义行为,每个包都有不同的可见性和范围规则。(这有时候也会让开发人员选择困难)
架构师必须意识到开发者是如何组织包的,如果几个包紧密的耦合在一起,那么重用其中一个包就变得非常困难。
鉴于模块化的重要性,研究人员提供了各种语言无关的标准来衡量,我们专注于三个关键概念:
内聚(Cohesion)
耦合(Coupling)
共生性(Connascence)(注:参考《UML面向对象设计基础》的翻译)
2.内聚(Cohesion)
内聚性是指子程序中各种操作之间联系的紧密程度,我们的目标是让每一个模块只做好一件事,不去做其他事情。
试图分割一个内聚的模块只会导致耦合性增加和可读性降低。(Attemptingtodivideacohesivemodulewouldonlyresultinincreasedcouplinganddecreasedreadability.)——LarryConstantine
?
计算机科学家们已经定义了一系列的内聚的衡量标准,从最好到最坏列出如下:
功能性内聚(Functionalcohesion):模块内所有元素都为完成同一个功能而存在,共同完成一个单一的功能,模块已不可再分,具有最高的内聚;
顺序内聚(Sequentialcohesion):模块必须顺序执行;
通信内聚(Communicationalcohesion):两个不同操作的模块使用同样的数据。例如,在数据库中添加一条记录,并根据该信息生成一封邮件;
过程内聚(Proceduralcohesion):两个模块必须以特定的次序执行;
时间内聚(Temporalcohesion):把需要同时执行的动作组合在一起形成的模块。
逻辑内聚(Logicalcohesion):这种模块把几种相关的功能组合在一起,每次被调用时,由传送给模块参数来确定该模块应完成哪一种功能。
巧合内聚(Coincidentalcohesion):模块内的各个元素之间没有任何联系,只是偶然地被凑到一起;内聚程度最低。
?
内聚不容易考量,特定的模块需要架构师来具体决定,例如,考虑一个模块定义了:
Customer:
addcustomer
updatecustomer
getcustomer
notifycustomer
getcustomerorders
cancelcustomerorders
或者可以说将后两个函数剥离出来,分成两个模块:
Customer:
addcustomer
updatecustomer
getcustomer
notifycustomer
Order:
getcustomerorders
cancelcustomerorders
哪个更好?一如既往,这要看情况:
订单只有这两个操作吗?如果是这样,将这些操作放在客户包中维护可能是有意义的;
客户包按预期是否会变得更大?
订单是否需要如此多的客户信息?
这些问题代表了软件架构师工作核心的权衡分析。
由于内聚非常主观,计算机科学家制定了一个标准来衡量内聚性,其中LCOM(LackofCohesioninMethods)为著名。这里涉及到的数学公式平时很少用到,在此不再展开,只需要知道有这么一个公式,在需要的时候可以再查询拿出来用。想进一步了解的读者可以查看: