瑞星卡卡安全论坛技术交流区系统软件 【转载】软件开发人员必备工具书 【代码大全】

«56789101112»   9  /  16  页   跳转

【转载】软件开发人员必备工具书 【代码大全】

数表进行传递,可以采用对其中所有数据进行直接存取来实现。
    从所有模块中的子程序可以对它进行存取的角度来说,模块中数据很像是全局数据。但从
不是程序中所有的子程序都可以对它进行存取的角度来说,它又不像是全局数据,它只对模块
中的子程序来说,才是可以存取的。因此,在模块设计中的最重要决定之一,便是决定哪个子
程序需要对模块中数据进行直接存取。如果某个子程序仅仅是由于可以对模块中数据进行存取
的原因才留在模块中的,那么,它应该被从模块中去掉。
6.2  信息隐蔽
      如果你阅读了书中所有推荐参阅文献的注释,你就会发现其中有400 多个是关于信息隐
蔽的。拥有这么多参考文献的内容一定是非常重要的吧?是的,它的确非常重要。
    进行信息隐蔽的设计思想贯穿了软件开发的每一个层次,从使用命名的常量而不是使用自
由常量到子程序设计、模块设计和整个程序设计。由于这一思想往往是在模块这一层次得到最
充分体现的。因此,我们在本章详细讨论它。
    信息隐蔽是为数不多的几个在实践中无可辩驳地证明了自己价值的理论之一(Boehm
1987)。研究发现,应用信息隐蔽进行设计的大型程序容易更改指数要比没采用这一技术的高4
倍。同时,信息隐蔽也是结构化设计和面向对象设什的基础之一。在结构化设计中,黑盒子思
想便来源于信息隐蔽。在面向对象设计中,也是信息隐蔽引发了抽象化和封装化的设计思想。
6.2.1  保密
    信息隐蔽中的关键概念是“保密”。每一个模块的最大特点都是通过设计和实现,使它对其
它模块保密。这个秘密或许是可能被改动的区域、某个文件的格式化、一个数据结构的实现方
式、或是一个需要与程序其它部分隔离开来,以便其中的错误产生的危害最小的区域。模块的
作用是将自己的信息隐蔽起来以保卫自己的隐私权。信息隐蔽的另一个称谓是“封装”,其意思
是一个外表与内容不一样的盒子。
    无论管它叫什么,信息隐蔽都是设计子程序和模块的一种方法,它对模块的意义更重要些。
当你隐藏秘密时,你就设计了一组存取同一套数据的子程序。对一个系统的改动可能涉及到几
个子程序,但是,它只应涉及一个模块。
    在设计模块时,一项重要任务就是决定哪些特性应该是对模块外部公开的,哪些应该是作
为秘密隐藏起来的,一个模块中可能使用25 个子程序而只暴露出其中的5 个,其余20 个都只
在内部使用。模块中也可能用到了几个数据结构,但却把它们全部隐藏起来。它可能会也可能
不会提供把数据结构信息通知给程序其余部分的子程序。模块设计的这一方面一般被称作“可
见性”,因为它主要涉及了模块的功能特性是否是对外部分开或暴露的。
模块的接口应该尽可能少地暴露它的内部内容。一个模块应该像是一座冰山,你只看到它
的一角,而它其余7/8 的部分则藏在水面下。
与设计的其它方面一样,设计模块的接口也是一个逐渐的过程,如果接口在第一次是不正
确的,可以再试几次直到它稳定下来;如果它稳定不下来,那么就需要重新设计它。
可以用各种不同的图形来代表模块。模块表示图的关键是,它应该区分开仅供模块内部使
用的功能和对外开放的功能。这种图形通常称之为“积木图”,是由Erody Boock 在开发Ada
gototop
 

语言过程中提出来的。图6-1 表示出了一种模块图。


    其中公用部分是矩形块,个别部分如黑盒子那样表示。
信息隐蔽不必暗示出一个系统的形状;系统可能具有分层结构,也可能像图6-2 中所示那
样具有网状结构。

    在网状结构中,你只要规定哪些模块可以与其它模块通信,这种特定的通信是如何进行的,
然后再进行联接的就可以了,如图6-3 所示,积木图也可以用在网状结构中。

                     
图6-1  一个模块中公用和个别部分
图6-2  网状结构系统
图6-3  用包含信息隐蔽思想的符号表示网状系统

附件附件:

下载次数:224
文件类型:application/octet-stream
文件大小:
上传时间:2006-8-22 19:08:24
描述:



gototop
 

6.2.2  信息隐蔽举例
几年前我曾写了一个中型系统(有20K行代码),在其中广泛使用了链表结构。问题域是由
数据结点构成的,每一个结点又与亚坐标、实坐标和等同点相联接。由于我选用了链表结构,
因此在程序中到处都是类似这样的语句:
node = node.next
and
phone = node.data.phone
这些语句直接对链表数据结构进行操作。尽管链表非常自然地将问题进行了模块化,但是
这种方法对内存的使用效率却非常低,于是我想使用整型数组索引来代替内存指针,因为这样
可以提高内存利用率,并且为在其它区域进行性能优化创造机会。但是,由于刚才提到的那种
编码语句充满了程序,因而修改工作非常困难。因为我无法在20000 多行代码中把它们一一找
出来。如果当初我采用了含有如下存取子程序的模块的话,我只要在一个地方即存取子程序中
改动代码就可能了。
      node = NearestNeighbor(node)
phone = EmergencyContact(node)
    我到底赢得了多少内存?我将会赢得或者失去多少速度,我不知道,但是如果当初我隐含
了数据结构的细节并且使用了存取子程序,我就可以很容易地找到答案。而且,我还可以尝试
一下另外几种方法。我本来可以从许多方案中挑选一个最好的,可是,由于我把数据结构的细
节暴露给了整个程序,我不得不使用我所厌恶的方案。
    除了方便修改,隐含复杂数据结构细节的另一个重要原因是:隐含细节可以澄清你编写某
段代码的意图。在上例中,一个富有经验的程序员不难读懂下面这条语句的:
      node = node.next
    显然,这个语句指的是一个链表结构,但除此之外,它什么也不能告诉你。然而,一个像
node = NearestNeighbour(node)这样的存取子程序,则清楚描述了链表所代表的内容,因而这是
很有用的,并且提醒你应该对node 这个名称进行改进(node 与其邻居有什么关系),node =
node.text这样的语句与实际相脱离,你根本无法想到应该改进它们的名称以说明实际问题。
    隐含数据结构的最后一个原因是出于对可靠性的考虑。如果你用一个专门的子程序来存取
数据结构,你只需在其中设置一个安全验证就可以了。否则,你就不得不在所有这个子程序访
问变量的地方设置安全验证。比如,如果你使用了链表,并且想读取链表中的下一个元素,并
且要注意不超过链表的最后一个元素,你可能用如下的代码:
        if ( node.text<>null ) then
node = node.text
    如果在某种情形下,为了更谨慎一些,你可以使用如下代码:
      if ( node<>null ) then
if ( node.next<>null ) then
gototop
 

node = node.text
    如果你的程序中充斥着node=node.next 这样的语句,你可能要在其中某些地方进行测试而
跳过其余部分。但是,如果这个操作是独立在一个子程序调用中:
      node=NearestNeighbor(node)
    那么,你只要在子程序中一个地方进行测试,那么这一测试就会在整个程序中都起作用。
如果有时你想在整个程序中都对使用node 的地方进行测试,也很容易漏掉其中某些地方。然而,
如果你把这一操作独立在一个子程序中,那么是不可能有遗漏的,因为此时这项工作完全是自
动进行的。
    隐含数据结构细节的另一个好处是容易调试,比如,你发现node值在某处变得有问题了,
但却不知道是在哪里。如果存取node 的代码充斥了整个程序的话,那么找到出问题的地方不亚
于大海捞针。但如果它是被孤立在一个子程序中的话,那么你可以在其中加入一段检查node
的调试码,从而在每次存取它时都进行测试,这样事情就解决了。
应用存取子程序最后一个优点是,可以使所有对数据的存取所遵循的是一种平行的组织形
式;或者通过存取子程序、或者直接对数据进行存取,不会两者兼而有之。当然,在负责数据
的模块内部,对数据的存取都是直接的,在这种情况下失去平行性是不可避免的,这样做的目
的是不在公共场合吹脏肥皂泡。这常常伴随着对在存取程序中进行直接数据操作这一拙劣设计
的隐含。
6.2.3  常见需要隐含的信息
    在你所从事的项目中,你可能与不计其数需要隐含的信息打交道,但是,其中只有几种是
你要反复遇到的:
· 容易被改动的区域
· 复杂的数据
· 复杂的逻辑
· 在编程语言层次上的操作
以上每一项都将在下面的部分中给予详细论述。
    容易被改动的区域
容易改动是好的程序设计中一项最富于挑战性的工作。目的是将不稳定的区域孤立起来,
以便使改动带来的影响仅限于一个模块中。以下是你在为应付改动的工作中要遵循的步骤。
    1. 识别出那些可能被改动的地方。如果分析工作做得很好的话,其中应该附有可能改动
的地方和改动内容的明细表。在这种情况下,找出可能的改动是非常容易的。如果需
求分析中没有进行这项工作,可以参阅下面关于在任何项目中都可能被改动的区域的
讨论。
    2. 把可能被改动的地方分离出来。把第一步中发现的每一个可能改动的地方分隔到自己
的模块中,或者将其与其它可能一起被改动的要素一起,独立到一个模块中。
    3. 独立可能被改动的地方。应把模块间的接口设计成对可能变动不敏感,同时,接口应
该把变动限制在模块内部,外部不会受到内部变动影响。而其它调用这个被改动过模
块的模块,不应感受到这个模块被修改过。模块的接口应该能保护模块的隐私权。
gototop
 

以下是一些可能变动的区域:
对硬件有依赖的地方。对于监视器、打印机、绘图机等,要清楚在尺寸、颜色、控制代码、
图形能力及内存等方面可能的变化。其余对硬件有依赖性的方面包括与磁盘、磁带、通讯口、
声音器件等接口的变化等等。
输入和输出。在比原始的硬件接口稍高一些的设计层次上,输入/输出是另外一个反复无常
的区域。如果某一应用产生它自己的数据文件,那么当这一应用变得复杂起来时,文件的格式
可能也要变化。用户层次上的输入和输出格式也有可能变化,比如,在打印纸上边界的位置、
每页上边界的数量、域的排列顺序等等。总之,检查所有的外部接口以寻找可能的变化是个好
主意。
非标准语言特性。如果在程序中使用了非标准扩展,应该把这些扩展隐含在一个模块中,
以便当运行环境变化时你可以很容易地替换它。同样,如果你使用了不是在所有环境下都存在
的库子程序,应该把实际的库子程序放在另一个环境下也可以使用的接口后面。
难于设计和实现的域。最好把难于设计和实现的域隐含起来,因为此处的工作可能作得很
糟,你可能不得不返工。把它们分隔起来,以便使由于拙劣设计或实现对系统所带来的危害最
小。
状态变量。状态变量指示程序的状态,往往比其它数据更容易被改动。在典型的情形下,
你可能最初把某一错误状态变量定义成逻辑变量。但后来又发现如果把它赋成具有 NoError,
WarningError和FatalError三个值的枚举型变量来实现会更好。
你至少可以在使用状态变量时,加上两个层次的灵活性和可读性。
·  不要使用逻辑型变量作为状态变量,应使用枚举型变量。赋予状态变量一种新状态是
非常常见的,给枚举型变量赋一个新的类型只需要重新编译一次,而对于逻辑型变量
则需要重新编写每行检查状态变量的代码,谁难谁易是很明显的。
·  使用存取子程序检查变量,而不要对其直接检查,通过检查存取子程序而不是状态变
量,可以进行更复杂的状态测试。例如,如果想检查一个错误状态变量和一个当前函
数状态变量,那么把测试隐含在子程序中来进行,要比用充斥着程序的复杂代码进行
测试容易得多。
数据规模限制。如果你说明一个数组中含有 15 个元素,那么你就把系统不需要的信息暴
露给了它。应该保护其隐私权,信息隐蔽并不总是意味着把一系列功能装入模块这类复杂的工
作,有时,它简单到就是用一个像MAX_EMPLOYEES 之类的常量来代替15,以便隐含它。
商业规则。商业规则指法律、政策、规定、惯例等编入一个计算机系统中的东西。如果你
在编写一个工资发放系统,你可能把IRS 关于允许的扣留数和估计税率等规则编入程序。其余
附加的规则是由工会规定的关于加班率、节假日付酬等方面的规定。如果你正在编写一个引用
保险率的软件,其规定来源于州关于信誉、实际保险率等的管理规定。
这些规定往往是数据处理系统中频繁变动的部分。因为国会可能修改法律,保险公司会调
整保险率。如果你遵从信息隐蔽原则,那么当规则变动时,建立在这些规则上的逻辑关系不会
完全垮掉。这些逻辑关系会隐藏在系统中唯一一个阴暗角落里,直到需对其作出改动为止。
预防到改动。当考虑一个系统中潜在的改动时,应该按照使得改动范围或大小与其改动可
能性成反比的原则来设计系统。如果改动很可能发生,要确保系统可以容易地容纳这一特征。
只有极其不可能发生的变动,才应该被允许在变动时,会影响到系统中一个以上的模块。
gototop
 

一个寻找可能发生变动域的技术是,首先分析程序中可能会被用户用到的最小的子单元,
这些子单元组成了程序的核心,而且很可能被改变。其次,规定对系统的最小增值。它们可以
小到看起来完全是琐碎的程度。这些潜在改进域组成了对系统的潜在改进。应使用信息隐蔽原
则对这些域进行设计。首先分析核心,可以发现哪些要素事实上是后加上的,从而从那里推测
并隐含改进。
复杂的数据
    所有的复杂数据都很可能被改动;如果它很复杂而对它使用得又很多,那么在实现层次
上与其打过交道后,可能会发现实现它的更好方式。如果应用信息隐蔽来隐含数据实现,就
可以付出较少的努力而获得更好的实现方法。如果不是这样,那么你每次与这些数据打交道
时,你可能都会在后悔,如果当初进行了信息隐蔽,改动实现将会是多么容易啊!
对复杂数据的使用程度,主要取决于程序。如果是一个只有几百行代码的小程序,你想
在其中对变量进行直接操作,那就这样干吧,这样可能影响程序,但也可能不会。在担心由
于对数据直接操作而带来的维护问题之前,应首先考虑这个小程序的特点。如果你正在编写
一个大一些的程序或使用了全局数据,那么就该考虑使用存取子程序。
复杂的逻辑
隐含复杂的逻辑可以改善程序的可读性。复杂的逻辑并不总是程序的最主要方面,把它
隐含起来可以使得子程序的活动更清楚。与复杂数据一样,复杂逻辑也是很可能变动的部分。
所以,把程序的其它部分从这种变动里隔离出去是非常有益的。在某些情况下,你可以将所
使用的逻辑种类隐含起来,例如,你可以通过一个大的if语句、case 语句或查表方式来进行
测试。除了这些进行测试的代码外,其余的代码不需要知道这些细节。如果程序中的其余代
码只需要知道结果,那么你就应该仅仅告诉它们结果。
在程序语言层次上的操作
你的程序越是像一个实际问题的解决方案,它就越是不像程序语言结构的组合,那么,
其质量也就越好,应该把过于专业化的信息隐含起来,比如,下面的语句:
  EmployeeID = EmployeeID+1
CurrentEmployee = EmployeeList [ EmployeeID ]
这是一段很不错的程序,但是它是用过于专业化的语言来表达的,应该用较高程度抽象
的语言来进行这个操作:
      CurrentEmployee = NextAvailableEmployee()
    或者用:
      CurrentEmployee = NextAvailableEmployee( EmployeeList, EmployeeID )
通过加入一个隐含了用专业化语言解释正在发生什么的子程序,使得在一个更高的抽象
层次上处理这个问题。这使得你的意图更清楚,而且使得代码更容易理解和改动了。
如果用图表来实现一个排序问题。函数HighestPriorityEvent(),LowestPriorityrEvent()和
NextEvent()是抽象函数,隐含了实现细节;而FrontOfQueue(),BackOfQueue()和NextInQueue()
gototop
 

并没有隐含多少细节,因为它们提到了实现,暴露了它们该隐藏的秘密。
一般来说,在设计一组在程序语言语句层次上操作数据的子程序时,应该把对数据操作
隐含在子程序组中,这样程序的其余部分就可能在比较抽象的层次上处理问题了。
6.2.4  信息隐蔽的障碍
绝大多数信息隐蔽障碍都是心理上的,它主要来自于在使用其它技术时形成的习惯。但
在某些情况下,信息隐蔽也的确是不可能的,而一些看起来像是隐蔽障碍的东西,但仅仅是
借口而已。
信息过度分散
    信息隐蔽的一个常见障碍是系统中信息过于分散。比如在一个系统中到处分布着常数
100。把100 当作一个常数,降低了引用它的集中程度。如果把信息隐蔽在一个地方会更好,
因为这样它的值将只在一个地方改变。
    另一个信息过于分散的例子是程序中分布着与用户交互的接口。如果需要改变交互方式,
比如,从命令行方式改为格式驱动方式,那么所有的代码事实上都要被改动。因此,最好把用
户交互接口放入一个单独的模块中,这样,你不必影响到整个系统就可以对交互方式进行改动。
    而还有一个例子则是全局数据结构,比如,一个在整个系统中四处被存取的拥有多达1000
个元素的雇员数据数组。如果程序直接使用这个全局数据,那么这个数据结构的实现信息——
它是一个数组且拥有最多1000 个元素——将充斥着整个程序。如果这个程序只通过存取子程
序来使用这个数据结构,那么就只有这个存取子程序才知道这些细节。
交叉依赖
    一个不易察觉的信息隐蔽障碍是交叉依赖。比如模块A 中的某一部分调用了模块B 中的
一个子程序,而模块B 中又有一部分调用了模块A 中的子程序。应避免这种交叉依赖现象。
因为只有在两者都已准备好的情况下,你才能测试其中的一个。当程序被覆盖时,必须使A
和B 同时驻留在内存中,才能避免系统失败。通过寻找两个模块中被其它模块使用的部分,
把这些部分放入新的模块A’和B’中,用模块A 和B 中的其余部分来调用A’和B’,基本上可
以消除这一问题。
误把模块数据当成全局数据
    如果你是个谨慎的程序员,那么信息隐蔽的障碍之一便是误把模块数据当作全局数据而
避免使用它,因为要避免由于使用全局数据而带来的麻烦。但是,如同在6.1 节“模块化:
内聚性与耦合性”中所说的那样,这两种数据是不同的。由于只有在模块中的子程序才可以
对其进行存取,因而由模块数据带来的麻烦要比全局数据小得多。
    如果不使用模块数据,就不会知道了解由模块所带来的巨大收益。如果一个子程序向模
块传递了只有它才能处理的数据的话,那么就不该由模块来承担拥有数据集合并对其进行操
作的罪责。比如,在前面列举的建议利用如下语句来提高抽象程度的例子中:
    CurrentEmployee = NextAvaliableEmployee()
    或使用:
gototop
 

这两个赋值语句间的区别是:在第一种情形下,NextAvailableEmployee()拥有关于雇员表
和目前表中的入口是哪一个入口的信息,而在第二种情况下,NextAvailableEmployee
(EmployeeList, EmployeeID)只是从向它传递数据的子程序中借用这些信息。当你使用
NextAvailableEmployee()时,为了提供全部的抽象能力,不必担心它所需要的数据,只要记住
它负责自己的问题就可以了。
  全局数据主要会产生两个问题:(1)一个子程序在对其进行操作时并不知道其它子程序也
在对它进行操作;(2)这个子程序知道其它子程序也在对其进行操作,但不知道它们对它干了
什么。而模块数据则不会产生这些问题,因为只有被放在一个单独模块中的有限几个子程序
才被允许对模块数据进行直接存取操作,当一个子程序进行这种操作时,它知道别的子程序
也在进行同样操作,并确切知道这些是哪几个子程序。如果你还不相信的话,试一下,结果
会令你满意的。
误认为会损失性能
信息隐蔽的最后一个障碍是在结构设计和编码两个层次上,都试图避免性能损失。事实
上,在两个层次上你都不必担心这一点。在结构设计层次上,这种担心之所以不必要是因为,
以信息隐蔽为目标进行结构设计,与以性能为目标进行结构设计是不矛盾的,只要你同时考
虑到这两点,那么就可以同时达到这两个目标。更常见的担心是在编码层次上,这种担心主
要是认为间接而不是直接地存取数据结构会带来运行时间上的损失,因为这样做附加了调用
层次。当测试了系统的性能并在瓶颈问题上有所突破时,这种担心是不成熟的。为提高软件
性能做准备的最好手段之一就是模块化设计,这样,在发现了更好的方案之后,不必改变系
统其余部分,就可以对个别子程序进行优化。
6.3    建立模块的理由
    即使不经常使用模块,凭直觉也很可能会对可以放入模块的数据和子程序种类有所了解。
从某种意义来说,模块并不是人们的目标,它只是数据及对数据所进行的操作的集合,并且支
持面向对象的概念——抽象和封装。模块不支持继承性,因而它也并不完全支持面向对象编程,
描述它的这种有限的面向对象特性的词汇是Booch 1991 年提出来的“基于对象”编程。
    以下是一些适合使用模块的域:
  用户接口。可以建立一个模块来把用户接口要素独立起来。这样,不会影响程序其它部分,
你就可以进行改进。在许多情况下,用户接口模块中往往包含有几个模块来进行诸如菜单操作、
窗口管理、系统帮助等。
对硬件有依赖的区域。把对硬件有依赖的区域放入一个或几个模块中。这些区域常见的有:
与屏幕、打印机、绘图机、磁盘驱动器、鼠标等的接口。把这些对硬件有依赖的区域独立起来
可能帮助把程序移植到新环境下运行。设计一个硬件经常变动的程序时,这也是很有帮助的,
可以编写软件表模拟与特定硬件的交互作用,硬件不存在或不稳定时,让接口子程序与这些模
拟软件打交道。然后在硬件稳定时,再让接口子程序与硬件打交道。
输入与输出。把输入/输出封装起来,可以使程序其余部分免受经常变动的文件和报告
格式的影响。把输入/输出放入模块,也使得程序很容易适应输入/输出设备的变动。
  操作系统依赖部分。把对操作系统有依赖的部分放入模块的原因与把对硬件有依赖部分放
入模块的原因是相同的。如果你正在编写一个在Microsoft Windows下运行的软件,为什么要把
它局限于Windows环境下呢?你完全可以把对Windows的调用放在一个Windows接口模块中。
如果以后想把程序移植到Macintosh或者OS/2环境下,你所要做的只是改动一下接口模块而已。
gototop
 

数据管理。应把数据管理部分放入模块中,让其中的子程序去与那些杂乱的实现细节打交
道。而让模块外的子程序用抽象的方式与数据打交道,这种方式应该尽可能避免实际处理问题,
如果你认为将数据管理模块化是将其放入一个单独模块中,那你就错了。通常,每一种主要的
抽象数据类型,都需要一个单独的模块来管理。
  真实目标与抽象数据类型。在程序中,需要为每个真实目标创立一个模块。把这一目标所
需要的数据放入模块中,然后再在其中建立对目标进行模块化的子程序。这就是所谓抽象数据
类型。
  可再使用的代码。应把计划在其它程序中再用的程序部分进行模块化。建立模块的一个优
点是,重新使用模块要比重新使用面向功能的程序实用得多。在面向对象设计和面向功能设计
方法中,刚开始的项目都不能从以前的项目中借用许多代码,因为以前项目还不够多,无法提
供足够的代码基础。使用面向功能设计方法开发的程序,大约可以从以前的项目中借用35%的
代码:而在使用面向对象设计方法开发的项目中,则大约可以从以前的项目中借用70%的代码。
如果可以通过深思远虑而在以后的项目中避免重写70%的代码,那为什么不这样做呢?
  可能发生变动的相互联系的操作。应该在那些可能发生变动的操作周围修建一道隔墙。这
事实上是容错原则的一种,因为这样可以避免局部的变动影响到程序的其余部分。在6.2 节中,
给出了一些经常发生变动的区域。
  互相联系的操作。最后,应把互相联系的操作放到一起。在绝大多数情况下,都可以发现
把看起来互相联系的子程序和数据放在一起的更强的组织原则。在无法隐蔽信息的情况下,比
如共享数据或计划增强灵活性时,仍然可以把成组操作放在一起,比如,三角函数、统计函数、
字符串操作子程序、图像子程序等。通过精心地成组放置相关操作,还可以在下一个项目中重
新使用它。
6.4  任何语言中实现模块
  有些语言直接支持模块化,但有些语言则需要补充一些编程标准才可以。
6.4.1  模块化所需的语言支持
  模块包括数据、数据类型、数据操作以及公共和局部操作的区分等。为了支持模块化,一
种语言必须支持多种模块。如果没有多模块,其它任何要求都是空谈。
  数据需要在三个层次上可以被存取和隐含,在局部,在模块中及在全局中,绝大多数语言
都支持局部数据和全局数据。如果想要使某些数据仅对模块中的子程序才是可以存取的,那么
就要求语言支持模块数据,即只有某些而不是全部子程序都可以存取的数据。
gototop
 

含在某一特定模块中,而另一些类型应该是对其它模块开放的。模块需要能够对那些可以知道
其它类型的模块进行控制。
    对模块层次上的子程序的要求也与上述相类似。有些子程序应该只有在模块内部才能调
用,而且模块应该对某一子程序是专用的还是公用的可以进行控制。在模块之外,不应该有其
它模块或子程序知道这个模块中存在专用子程序。如果模块设计得很好,那么其它模块或子程
序不应该有任何理由来关心专用子程序的存在。
6.4.2  语言支持慨述
    在下表中,对几种语言支持信息隐蔽的必要结构进行了总结:
  通用Basic 和通用Pascal 不支持多模块,所以被排在支持模块化的前列,Fortran 和
QuickBasic 不能控制支持模块化的数据类型。只有Ada、C++和C 以及Turbo Pascal才允许模块
限制对某一子程序的调用,从而使这个子程序真正是专用的。
  简而言之,除非使用Ada、C、C++或Turbo Pascal,否则,就不得不通过命名或其它约
定来扩充所使用语言的能力,以便模拟使用模块。以下部分简单论述了直接支持模块化的语
言,并且将告诉你在其它语言中怎样模拟使用模块。
    Ada 与Modula-2 支持
    Ada通过“包”的概念来支持模块化。如果用Ada编过程序,那么就已经知道如何建立包
了。Modula-2 通过模块的概念来支持模块化。虽然Modula-2 并不是本书的特性,它对模块化
的支持仍然是非常直接的,以下给出了一个用Modula-2进行排序的例子:
    definition module Events;
export
EVENT,
EventAvailable,
HighestPriorityEvent,
LowestPriorityEvent;
 
数据数据类型源程序
语言多模块
局部模块全局局部模块全局专用模块/全局
Ada · · · · · · · · ·
C · · · · · +  · · ·
C++ · · · · · · · · ·
Fortran77 + · + · -  - - - ·
通用Basic - - - · - - - - ·
通用Pascal · · · · · · · · ·
Turbo Pascal - · - · · - · - ·
QuickBasic · · · - · - - - ·
这些是公共的
gototop
 
«56789101112»   9  /  16  页   跳转
页面顶部
Powered by Discuz!NT