第14章(第1 / 2页)
(2)逻辑内聚。几个逻辑上相关的功能被放在同一模块中,则称为逻辑内聚。如一个模块读取各种不同类型外设的输入。尽管逻辑内聚比偶然内聚合理一些,但逻辑内聚的模块各成分在功能上并无关系,即使局部功能的修改有时也会影响全局,因此这类模块的修改也比较困难。
(3)时间内聚。如果一个模块完成的功能必须在同一时间内执行(如系统初始化),但这些功能只是因为时间因素关联在一起,则称为时间内聚。
(4)过程内聚。如果一个模块内部的处理成分是相关的,而且这些处理必须以特定的次序执行,则称为过程内聚。
5.2.1信息隐藏
在一节不和谐的课堂里,老师叹气道:“要是坐在后排聊天的同学能象中间打牌的同学那么安静,就不会影响到前排睡觉的同学。”
这个故事告诉我们,如果不想让坏事传播开来,就应该把坏事隐藏起来,“家丑不可外扬”就是这个道理。为了尽量避免某个模块的行为去干扰同一系统中的其它模块,在设计模块时就要注意信息隐藏。应该让模块仅仅公开必须要让外界知道的内容,而隐藏其它一切内容。
模块的信息隐藏可以通过接口设计来实现。一个模块仅提供有限个接口(Interface),执行模块的功能或与模块交流信息必须且只须通过调用公有接口来实现。如果模块是一个C++对象,那么该模块的公有接口就对应于对象的公有函数。如果模块是一个COM对象,那么该模块的公有接口就是COM对象的接口。一个COM对象可以有多个接口,而每个接口实质上是一些函数的集合。[Rogerson1999]
美国也许是世界上丑闻最多的国家,因为它追求民主,不懂得“隐藏信息”。但美国又是软件产业最发达的国家,模块化设计的方法都是美国人倡导的,他们应该很懂得“隐藏信息”。真是前后矛盾,这些美国佬!
为了理解这一个问题,设想一个人试图在分离的机器上启动两个程序并让它们进行通讯。还需记住,计算机的运行速度要比人的操作速度高出许多数量级。在他启动第一个程序后,该程序开始执行并向对等程序发送消息。在几个微秒内,它便发现对等程序还不存在,于是就发出一条错误消息,然后退出。此后,他启动了第二个程序。不幸的是,当第二个程序开始执行时,它也找不到第一个程序(早已退出)。即使这两个程序连续地重新试着通讯,但由于它们的执行速度那么高,以致于它们在同一瞬间联系上的概率非常低。在客户机/服务器结构中,服务器在启动后必须(无限期地)等待客户机的“请求”,因此就形成了“请求——响应”的通讯方式。
在Internet/Intranet领域,目前“浏览器—Web服务器—数据库服务器”结构是一种非常流行的客户机/服务器结构,如图5.6所示。这种结构最大的优点是:客户机统一采用浏览器,这不仅让用户使用方便,而且使得客户机端不存在维护的问题。当然,软件开发布和维护的工作不是自动消失了,而是转移到了Web服务器端。在Web服务器端,程序员要用脚本语言编写响应页面。例如用Microsoft的ASP语言查询数据库服务器,将结果保存在Web页面中,再由浏览器显示出来。
HTTP请求
查询
HTTP响应
5.2.2内聚与耦合
内聚(Cohesion)是一个模块内部各成分之间相关联程度的度量。耦合(Coupling)是模块之间依赖程度的度量。内聚和耦合是密切相关的,与其它模块存在强耦合的模块通常意味着弱内聚,而强内聚的模块通常意味着与其它模块之间存在弱耦合。模块设计追求强内聚,弱耦合。
一、内聚强度
内聚按强度从低到高有以下几种类型:
(1)偶然内聚。如果一个模块的各成分之间毫无关系,则称为偶然内聚。
图5.6“浏览器—Web服务器—数据库服务器”结构
5.2模块设计
在设计好软件的体系结构后,就已经在宏观上明确了各个模块应具有什么功能,应放在体系结构的哪个位置。我们习惯地从功能上划分模块,保持“功能独立”是模块化设计的基本原则。因为,“功能独立”的模块可以降低开发、测试、维护等阶段的代价。但是“功能独立”并不意味着模块之间保持绝对的孤立。一个系统要完成某项任务,需要各个模块相互配合才能实现,此时模块之间就要进行信息交流。
比如手和脚是两个“功能独立”的模块。没有脚时,手照样能干活。没有手时,脚仍可以走路。但如果希望跑得快,那么迈左脚时一定要伸右臂甩左臂,迈右脚时则要伸左臂甩右臂。在设计一个模块时不仅要考虑“这个模块就该提供什么样的功能”,还要考虑“这个模块应该怎样与其它模块交流信息”。
本节将论述评价模块设计优劣的三个特征因素:“信息隐藏”、“内聚与耦合”和“封闭——开放性”。