图5.6“浏览器一Wb服务器一数据库服务器”结构 5.2模块设计 在设计好软件的体系结构后,就已经在宏观上明确了各个模块应具有什么功能,应 放在体系结构的哪个位置。我们习惯地从功能上划分模块,保持“功能独立”是模块化 设计的基本原则。因为,“功能独立”的模块可以降低开发、测试、维护等阶段的代价。 但是“功能独立”并不意味若模块之间保持绝对的孤立。一个系统要完成某项任务,需 要各个模块相互阳合才能实现,此时模块之间就要讲行信息交流。 比如手和脚是两个“功能独立”的模块。没有脚时,手照样能干活。没有手时,脚 仍可以走路。但如果希望跑得快,那么迈左脚时一定要伸右臂甩左臂,迈右脚时则要伸 左臂甩右臂。在设计一个模块时不仅要考虑“这个模块就该提供什么样的功能”,还要考 虑“这个模块应该怎样与其它模块交流信息”。 本节将论述评价模块设计优劣的三个特征因素:“信息隐藏”、“内聚与耦合”和“封 闭 一开放性”。 5.2.1信息隐藏 在一节不和诺的课堂里,老师叹气道:“要是坐在后排聊天的同学能象中间打牌的同 学那么安静,就不会影响到前排睡觉的同学。” 这个故事告诉我们,如果不想让坏事传播开来,就应该把坏事隐藏起来,“家丑不可 外扬”就是这个道理。为了尽量避免某个模块的行为去干扰同一系统中的其它模块,在 设计模块时就要注意信总隐藏。应该让模块仅仅公开必须要让外界知道的内容,而隐藏 其它一切内容 模块的信息隐藏可以通过接口设计来实现。一个模块仅提供有限个接口(Interface), 执行模块的功能或与模块交流信息必须且只须通过调用公有接口来实现。如果模块是 个C+对象,那么该模块的公有接口就对应于对象的公有函数。如果模块是一个CON 对象,那么该模块的公有接口就是COM对象的接口。一个COM对象可以有多个接口, 而每个接口实质上是一些函数的集合。[Rogerson1999 美国也许是世界上丑闻最多的国家,因为它追求民主,不懂得“隐藏信息”。但美 国又是软件产业最发达的国家,模块化设计的方法都是美国人倡导的,他们应该很懂得 “隐藏信总”。真是前后矛盾,这些美国佬! 5.2.2内聚与辋合 内聚(Cohesion)是一个模块内部各成分之间相关联程度的度量。耦合(Coupling》 是模块之间依赖程度的度量。内聚和耦合是密切相关的,与其它模块存在强耦合的模块 通常意味着弱内聚,而强内聚的模块通常意味着与其它模块之间存在弱耦合。模块设计 追求强内聚,弱耦合 一、内聚强度 内聚按强度从低到高有以下几种类型: 6
6 图 5.6 “浏览器—Web 服务器—数据库服务器”结构 5.2 模 块 设 计 在设计好软件的体系结构后,就已经在宏观上明确了各个模块应具有什么功能,应 放在体系结构的哪个位置。我们习惯地从功能上划分模块,保持“功能独立”是模块化 设计的基本原则。因为,“功能独立”的模块可以降低开发、测试、维护等阶段的代价。 但是“功能独立”并不意味着模块之间保持绝对的孤立。一个系统要完成某项任务,需 要各个模块相互配合才能实现,此时模块之间就要进行信息交流。 比如手和脚是两个“功能独立”的模块。没有脚时,手照样能干活。没有手时,脚 仍可以走路。但如果希望跑得快,那么迈左脚时一定要伸右臂甩左臂,迈右脚时则要伸 左臂甩右臂。在设计一个模块时不仅要考虑“这个模块就该提供什么样的功能”,还要考 虑“这个模块应该怎样与其它模块交流信息”。 本节将论述评价模块设计优劣的三个特征因素:“信息隐藏”、“内聚与耦合”和“封 闭——开放性”。 5.2.1 信息隐藏 在一节不和谐的课堂里,老师叹气道:“要是坐在后排聊天的同学能象中间打牌的同 学那么安静,就不会影响到前排睡觉的同学。” 这个故事告诉我们,如果不想让坏事传播开来,就应该把坏事隐藏起来,“家丑不可 外扬”就是这个道理。为了尽量避免某个模块的行为去干扰同一系统中的其它模块,在 设计模块时就要注意信息隐藏。应该让模块仅仅公开必须要让外界知道的内容,而隐藏 其它一切内容。 模块的信息隐藏可以通过接口设计来实现。一个模块仅提供有限个接口(Interface), 执行模块的功能或与模块交流信息必须且只须通过调用公有接口来实现。如果模块是一 个 C++对象,那么该模块的公有接口就对应于对象的公有函数。如果模块是一个 COM 对象,那么该模块的公有接口就是 COM 对象的接口。一个 COM 对象可以有多个接口, 而每个接口实质上是一些函数的集合。[Rogerson 1999] 美国也许是世界上丑闻最多的国家,因为它追求民主,不懂得“隐藏信息”。但美 国又是软件产业最发达的国家,模块化设计的方法都是美国人倡导的,他们应该很懂得 “隐藏信息”。真是前后矛盾,这些美国佬! 5.2.2 内聚与耦合 内聚(Cohesion)是一个模块内部各成分之间相关联程度的度量。耦合(Coupling) 是模块之间依赖程度的度量。内聚和耦合是密切相关的,与其它模块存在强耦合的模块 通常意味着弱内聚,而强内聚的模块通常意味着与其它模块之间存在弱耦合。模块设计 追求强内聚,弱耦合。 一、内聚强度 内聚按强度从低到高有以下几种类型:
(1)偶然内聚。如果一个模块的各成分之间毫无关系,则称为偶然内聚。 (2)逻辑内聚。几个逻辑上相关的功能被放在同一模块中,则称为逻辑内聚。如一个模 块读取各种不同类型外设的输入。尽管逻辑内聚比偶然内聚合理一些,但逻辑内聚的模 块各成分在功能上并无关系,即使局部功能的修改有时也会影响全局,因此这类模块的 修改也比较困难。 (3)时间内聚。如果一个模块完成的功能必须在同一时间内执行(如系统初始化),但 这些功能只是因为时间因素关联在一起,则称为时间内聚。 (4)过程内聚。如果一个模块内部的处理成分是相关的,而且这些处理必须以特定的次 序执行,则称为过程内聚。 (⑤)通信内聚。如果一个模块的所有成分都操作同一数据集或生成同一数据集,则称为 通信内聚。 (6)顺序内聚。如果一个模块的各个成分和同一个功能密切相关,而且一个成分的输出 作为另一个成分的输入,则称为顺序内聚。 (7)功能内聚。模块的所有成分对于完成单一的功能都是必须的,则称为功能内聚。 二、祸合强度 耦合的强度依赖于以下几个因素:(1)一个模块对另一个模块的调用:(2)一个模 块向另一个模块传递的数据量:(3)一个模块施加到另一个模块的控制的多少:(4)模 块之间接口的复杂程度。 耦合按从强到弱的顺序可分为以下几种类型: (1)内容耦合。当一个模块直接修改或操作另一个模块的数据,或者直接转入另一个模 块时,就发生了内容耦合。此时,被修改的模块完全依赖于修改它的模块。 (2)公共耦合。两个以上的模块共同引用一个全局数据项就称为公共耦合。 (3)控制耦合。 一个模块在界面上传递一个信号(如开关值、标志量等)控制另一个模 块,接收信号的模块的动作根据信号值进行调整,称为控制耦合。 (4)标记耦合。模块间通过参数传递复杂的内部数据结构,称为标记耦合。此数据结构 的变化将使相关的模块发生变化。 (5)数据耦合。模块间通过参数传递基本类型的数据,称为数据耦合。 (6)非直接耦合。模块间没有信息传递时,属于非直接耦合。 如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的 范围,坚决避免使用内容耦合。 52.3封闭一一开放样 如果一个模块可以作为一个独立体被其它程序引用,则称模块具有封闭性。如果 个模块可以被扩”充,则称模块具有开放性 从字面上看,让模块具有“封闭一 一开放性”是矛盾的,但这种特征在软件开发过 程中是客观存在的。当着手一个新问题时,我们很难一次性解决问题。应该先纵观问题 的一些重要方面,同时作好以后补充的准各。因此让模块存在“开放性”并不是坏事情。 “封闭性”也是需要的,因为我们不能等到完全掌握解决问题的信息后再把程序做成别 人能用的模块
7 (1)偶然内聚。如果一个模块的各成分之间毫无关系,则称为偶然内聚。 (2)逻辑内聚。几个逻辑上相关的功能被放在同一模块中,则称为逻辑内聚。如一个模 块读取各种不同类型外设的输入。尽管逻辑内聚比偶然内聚合理一些,但逻辑内聚的模 块各成分在功能上并无关系,即使局部功能的修改有时也会影响全局,因此这类模块的 修改也比较困难。 (3)时间内聚。如果一个模块完成的功能必须在同一时间内执行(如系统初始化),但 这些功能只是因为时间因素关联在一起,则称为时间内聚。 (4)过程内聚。如果一个模块内部的处理成分是相关的,而且这些处理必须以特定的次 序执行,则称为过程内聚。 (5)通信内聚。如果一个模块的所有成分都操作同一数据集或生成同一数据集,则称为 通信内聚。 (6)顺序内聚。如果一个模块的各个成分和同一个功能密切相关,而且一个成分的输出 作为另一个成分的输入,则称为顺序内聚。 (7)功能内聚。模块的所有成分对于完成单一的功能都是必须的,则称为功能内聚。 二、耦合强度 耦合的强度依赖于以下几个因素:(1)一个模块对另一个模块的调用;(2)一个模 块向另一个模块传递的数据量;(3)一个模块施加到另一个模块的控制的多少;(4)模 块之间接口的复杂程度。 耦合按从强到弱的顺序可分为以下几种类型: (1)内容耦合。当一个模块直接修改或操作另一个模块的数据,或者直接转入另一个模 块时,就发生了内容耦合。此时,被修改的模块完全依赖于修改它的模块。 (2)公共耦合。两个以上的模块共同引用一个全局数据项就称为公共耦合。 (3)控制耦合。一个模块在界面上传递一个信号(如开关值、标志量等)控制另一个模 块,接收信号的模块的动作根据信号值进行调整,称为控制耦合。 (4)标记耦合。模块间通过参数传递复杂的内部数据结构,称为标记耦合。此数据结构 的变化将使相关的模块发生变化。 (5)数据耦合。模块间通过参数传递基本类型的数据,称为数据耦合。 (6)非直接耦合。模块间没有信息传递时,属于非直接耦合。 如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的 范围,坚决避免使用内容耦合。 5.2.3 封闭——开放性 如果一个模块可以作为一个独立体被其它程序引用,则称模块具有封闭性。如果一 个模块可以被扩充,则称模块具有开放性。 从字面上看,让模块具有“封闭——开放性”是矛盾的,但这种特征在软件开发过 程中是客观存在的。当着手一个新问题时,我们很难一次性解决问题。应该先纵观问题 的一些重要方面,同时作好以后补充的准备。因此让模块存在“开放性”并不是坏事情。 “封闭性”也是需要的,因为我们不能等到完全掌握解决问题的信息后再把程序做成别 人能用的模块