子程序是具有一定功能的,可以调用的函数或过程;而模块则是指数据及作用于数据的子程序的集合。
6.1 模块化:内聚性与耦合性
“模块化”同时涉及到子程序设计和模块设计;模块化设计的目标是使每个子程序都成为一个“黑盒子”;使用单独一个子程序是很难达到这一目的的,这也正是引入模块的原因。
6.1.1 模块内聚性
模块的内聚性准则,与单个子程序的内聚性准则一样,都是十分简单的;一个模块应该提供一组相互联系的服务。
6.1.2 模块耦合
模块与程序其它部分间的耦合标准与子程序间的耦合标准也是类似的;模块应被设计成可以提供一整套功能,以便程序的其它部分与它清楚地相互作用。
如果模块所提供的功能是不完善的,其它子程序可能被迫对其内部数据进行读写操作。
为了设计出强内聚而又松散耦合的模块,必须在设计模块和设计单个子程序的标准之间进行平衡与折衷;降低子程序之间耦合性的重要措施之一,就是尽可能减少使用全局变量。
从所有模块中的子程序可以对它进行存取的角度来说,模块中数据很像是全局数据。
6.2 信息隐蔽
进行信息隐蔽的设计思想贯穿了软件开发的每一个层次,从使用命名的常量而不是使用自由常量到子程序设计、模块设计和整个程序设计。
6.2.1 保密
信息隐蔽中的关键概念是“保密”,每一个模块的最大特点都是通过设计和实现,使它对其它模块保密;模块的作用是将自己的信息隐蔽起来以保卫自己的隐私权;信息隐蔽的另一个称谓是“封装”,其意思是一个外表与内容不一样的盒子。
模块的接口应该尽可能少地暴露它的内部内容。
6.2.2 信息隐蔽举例
除了方便修改,隐含复杂数据结构细节的另一个重要原因是:隐含细节可以澄清你编写某段代码的意图。
隐含数据结构的最后一个原因是出于对可靠性的考虑。
隐含数据结构细节的另一个好处是容易调试。
应用存取子程序最后一个优点是,可以使所有对数据的存取所遵循的是一种平行的组织形式;或者通过存取子程序、或者直接对数据进行存取,不会两者兼而有之。
6.2.3 常见需要隐含的信息
容易被改动的区域:在为应付改动的工作中要遵循的步骤:1)识别出那些可能被改动的地方;2)把可能被改动的地方分离出来;一些可能变动的区域:1)对硬件有依赖的地方;2)输入和输出;3)非标准语言特性;4)难于设计和实现的域;5)状态变量;6)数据规模限制;7)商业规则;8)预防到改动。
复杂的数据:所有的复杂数据都很可能被改动;如果它很复杂而对它使用得又很多,那么在实现层次上与其打过交道后,可能会发现实现它的更好方式;对复杂数据的使用程度,主要取决于程序。
复杂的逻辑:隐含复杂的逻辑可以改善程序的可读性。
在程序语言层次上的操作:一般来说,在设计一组在程序语言语句层次上操作数据的子程序时,应该把对数据操作隐含在子程序组中,这样程序的其余部分就可能在比较抽象的层次上处理问题了。
6.2.4 信息隐蔽的障碍
信息过度分散:信息隐蔽的一个常见障碍是系统中信息过于分散;另一个信息过于分散的例子是程序中分布着与用户交互的接口;而还有一个例子则是全局数据结构。
交叉依赖:一个不易察觉的信息隐蔽障碍是交叉依赖。
误把模块数据当成全局数据:全局数据主要会产生两个问题:1)一个子程序在对其进行操作时并不知道其它子程序也在对它进行操作;2)这个子程序知道其它子程序也在对其进行操作,但不知道它们对它干了什么。
误认为会损失性能:信息隐蔽的最后一个障碍是在结构设计和编码两个层次上,都试图避免性能损失。
6.3 建立模块的理由
一些适合使用模块的域:1)用户接口;2)对硬件有依赖的区域;3)输入与输出;4)操作系统依赖部分;5)数据管理;6)真实目标与抽象数据类型;7)可再使用的代码;8)可能发生变动的相互联系的操作;9)互相联系的操作。
6.4 任何语言中实现模块
6.4.1 模块化所需的语言支持
模块包括数据、数据类型、数据操作以及公共和局部操作的区分等。
数据需要在三个层次上可以被存取和隐含:在局部,在模块中及在全局中;绝大多数语言都支持局部数据和全局数据。
对于数据类型的可存取性和可隐含性的要求,与对数据的要求是类似的。
对模块层次上的子程序的要求也与上述相类似。
6.4.2 语言支持概述
6.4.3 伪模块化
把数据和子程序装入模块;保证模块的内部子程序是专用的;通过在说明的地方加注释来明确区分公用和专用子程序;不允许子程序调用其它模块的内部子程序;采用命名约定来表明一个子程序是内部的还是外部的;采用表明它是内部的还是外部的命名规定;保证子程序的内部数据是专用的。
6.4.4 检查表
模块的质量。
6.5 小结
(1) 不管调用哪一个,子程序与模块的不同是很重要的,要认真考虑子程序与模块的设计。
(2) 从模块数据是被几个子程序使用的这一角度来说,它与全局数据是相同的,但从可以使用它的子程序是有限的,而且清楚地知道是哪些子程序可以使用它这一角度来说,模块数据与全局数据又是不同的;因此,可以使用模块数据而没有全局数据的危险。
(3) 信息隐蔽总是有益的,其结果是可以产生可靠的易于改进的系统,它也是目前流行的设计方法的核心。
(4) 创建模块的原因有许多是与创建子程序相同的,但模块概念的意义要比子程序深远得多,因为它可以提供一整套而不是单独一个功能,因此,它是比子程序更高层次的设计工具。
(5) 可以在任何语言中进行模块设计,如果所采用的语言不直接支持模块,可以用编程约定对其加以扩展,以达到某种程度的模块化。
本章小结:
本章介绍了模块化设计,与上一章的子程序相对应。
在实际的项目中,模块化的方法用得非常多。每一个开发人员所做的工作,其实就是完成了自己负责的一个模块。在开发工作完成之后,需要进行集成测试,也就是验证模块之间能否协同工作。