结构化方法由结构化分析、结构化设计、结构化程序设计构成,它是一种面向数据流的开发方法。结构化分析是根据分解与抽象的原则,按照系统中数据处理的流程,用数据流图来建立系统的功能模型,从而完成需求分析工作。结构化设计是根据模块独立性准则、软件结构优化准则将数据流图转换为软件的体系结构,用软件结构图来建立系统的物理模型,实现系统的概要设计。结构化程序设计使用3种基本控制结构构造程序,任何程序都可以由顺序、选择和重复3种基本控制结构构造。

结构化方法总的指导思想是自顶向下、逐层分解,它的基本原则是功能的分解与抽象。它是软件工程中最早出现的开发方法,特别适合于数据处理领域的问题,但是不适合解决大规模的、特别复杂的项目,且难以适应需求的变化。

本章的主要内容包括系统分析与设计知识、结构化分析与设计知识、WebApp 的分析与设计知识、用户界面设计知识。

6.1 系统分析与设计概述

6.1.1 系统分析概述

系统分析是一种问题求解技术,它将一个系统分解成各个组成部分,目的是研究各个部分如何工作、交互,以实现其系统目标。系统分析的目的是为项目团队提供对触发项目的问题和需求的更全面的理解,因此强调业务问题方面,而非技术或实现方面。系统分析阶段要求和系统用户一起工作,以便清楚地定义新系统的业务需求和预期。

1. 系统分析的目的和任务

系统分析的主要任务是对现行系统进一步详细调查,将调查中所得到的文档资料集中,对组织内部整体管理状况和信息处理过程进行分析,为系统开发提供所需的资料,并提交系统方案说明书。系统分析侧重于从业务全过程的角度进行分析,主要内容有业务和数据的流程是否通畅、是否合理;数据、业务过程和组织管理之间的关系;原系统管理模式改革和新系统管理方法的实现是否具有可行性等。

确定的分析结果包括开发者对于现有组织管理状况的了解,用户对信息系统功能的需求,数据和业务流程,管理功能和管理数据指标体系以及新系统拟改动和新增的管理模型等。

最后,提出信息系统的各种设想和方案,并对所有的设想和方案进行分析、研究、比较、判断和选择,获得一个最优的新系统的逻辑模型,并在用户理解计算机系统的工作流程和处理方式的情况下,将它明确地表达成书面资料——系统分析报告,即系统方案说明书。

2. 系统分析的主要步骤

企业信息系统是一个具有业务复杂性和技术复杂性的大系统,为的是目标系统既能实现当前系统的基本职能,又能改进和提高。系统开发人员首先必须理解并描述出已经实际存在的当前系统,然后进行改进,从而创造出基于当前系统又高于当前系统的目标系统,即新系统。

系统分析过程一般按如图6-1所示的逻辑进行。

(1)认识、理解当前的现实环境,获得当前系统的”物理模型”。

(2)从当前系统的”物理模型”抽象出当前系统的”逻辑模型”。

(3)对当前系统的”逻辑模型”进行分析和优化,建立目标系统的”逻辑模型”。

(4)对目标系统的逻辑模型具体化(物理化),建立目标系统的物理模型。

系统开发的目的是把现有系统的物理模型转化为目标系统的物理模型,即图6-1中双虚线所描述的路径,而系统分析阶段的结果是得到目标系统的逻辑模型。逻辑模型反映了系统的功能和性质,而物理模型反映的是系统的某一种具体实现方案。

按照图6-1,可将系统分析阶段的主要工作分为以下几步。

(1)对当前系统进行详细调查,收集数据。

(2)建立当前系统的逻辑模型。

(3)对现状进行分析,提出改进意见和新系统应达到的目标。

(4)建立新系统的逻辑模型。

(5)编写系统方案说明书。

6.1.2 系统设计的基本原理

1. 抽象

抽象是一种设计技术,重点说明一个实体的本质方面,而忽略或者掩盖不太重要或非本质的方面。抽象是一种重要的工具,用来将复杂的现象简化到可以分析、实验或者可以理解的程度。软件工程中从软件定义到软件开发要经历多个阶段,在这个过程中每前进一步都可看作是对软件解法的抽象层次的一次细化。抽象的最底层就是实现该软件的源程序代码。在进行模块化设计时也可以有多个抽象层次,最高抽象层次的模块用概括的方式叙述问题的解法,较低抽象层次的模块是较高抽象层次模块对问题解法描述的细化。

2. 模块化

模块在程序中是数据说明、可执行语句等程序对象的集合,或者是单独命名和编址的元素,例如高级语言中的过程、函数和子程序等。在软件的体系结构中,模块是可组合、分解和更换的单元。

模块化是指将一个待开发的软件分解成若干个小的简单部分——模块,每个模块可独立地开发、测试,最后组装成完整的程序。这是一种复杂问题”分而治之”的原则。模块化的目的是使程序的结构清晰,容易阅读、理解、测试和修改。

3. 信息隐蔽

信息隐蔽是开发整体程序结构时使用的法则,即将每个程序的成分隐蔽或封装在一个单一的设计模块中,在定义每一个模块时尽可能少地显露其内部的处理。在设计时首先列出一些可能发生变化的因素,在划分模块时将一个可能发生变化的因素隐蔽在某个模块的内部,使其他模块与这个因素无关。当这个因素发生变化时,只需修改含有这个因素的模块,而与其他模块无关。

信息隐蔽原则对提高软件的可修改性、可测试性和可移植性都有重要的作用。

4. 模块独立

模块独立是指每个模块完成一个相对独立的特定子功能,并且与其他模块之间的联系简单。衡量模块独立程度的标准有两个:耦合性和内聚性。

1)耦合

耦合是模块之间的相对独立性(互相连接的紧密程度)的度量。耦合取决于各个模块之间接口的复杂程度、调用模块的方式以及通过接口的信息类型等。一般模块之间可能的耦合方式有7种类型,如图6-2所示。

  • 无直接耦合。指两个模块之间没有直接的关系,它们分别从属于不同模块的控制与调用,它们之间不传递任何信息。因此,模块间耦合性最弱,模块独立性最高。
  • 数据耦合。指两个模块之间有调用关系,传递的是简单的数据值,相当于高级语言中的值传递。
  • 标记耦合。指两个模块之间传递的是数据结构。
  • 控制耦合。指一个模块调用另一个模块时,传递的是控制变量,被调用模块通过该控制变量的值有选择地执行模块内的某一功能。因此,被调用模块应具有多个功能,哪个功能起作用受调用模块控制。
  • 外部耦合。模块间通过软件之外的环境联结(如 I/O 将模块耦合到特定的设备、格式、通信协议上)时称为外部耦合。
  • 公共耦合。指通过一个公共数据环境相互作用的那些模块间的耦合。
  • 内容耦合。当一个模块直接使用另一个模块的内部数据,或通过非正常入口转入另一个模块内部时,这种模块之间的耦合称为内容耦合。
2)内聚

内聚是对一个模块内部各个元素彼此结合的紧密程度的度量。一个内聚程度高的模块(在理想情况下)应当只做一件事。一般模块的内聚性分为7种类型,如图6-3所示。

  • 偶然内聚(巧合内聚)。指一个模块内的各处理元素之间没有任何联系。
  • 逻辑内聚。指模块内执行若干个逻辑上相似的功能,通过参数确定该模块完成哪一个功能。
  • 时间内聚。把需要同时执行的动作组合在一起形成的模块称为时间内聚模块。
  • 过程内聚。指一个模块完成多个任务,这些任务必须按指定的过程执行。
  • 通信内聚。指模块内的所有处理元素都在同一个数据结构上操作,或者各处理使用相同的输入数据或者产生相同的输出数据。
  • 顺序内聚。指一个模块中的各个处理元素都密切相关于同一功能且必须顺序执行,前一功能元素的输出就是下一功能元素的输入。
  • 功能内聚。这是最强的内聚,指模块内的所有元素共同作用完成一个功能,缺一不可。

耦合性和内聚性是模块独立性的两个定性标准,在将软件系统划分模块时,应尽量做到高内聚、低耦合,提高模块的独立性。

6.1.3 系统总体结构设计

系统总体结构设计是要根据系统分析的要求和组织的实际情况对新系统的总体结构形式和可利用的资源进行大致设计,这是一种宏观、总体上的设计和规划。下面介绍系统总体设计的主要内容。

1. 系统结构设计原则

为保证总体结构设计顺利完成,应遵循以下几条原则。

(1)分解-协调原则。整个系统是一个整体,具有整体目的和功能,但这些目的和功能的实现又是由相互联系的各个组成部分共同工作的结果。解决复杂问题的一个很重要的原则就是把它分解成多个小问题分别处理,在处理过程中根据系统总体要求协调各部门的关系。

(2)自顶向下的原则。首先抓住系统总的功能目的,然后逐层分解,即先确定上层模块的功能,再确定下层模块的功能。

(3)信息隐蔽、抽象的原则。上层模块只规定下层模块做什么和所属模块间的协调关系,但不规定怎么做,以保证各模块的相对独立性和内部结构的合理性,使得模块与模块之间层次分明,易于理解、实施和维护。

(4)一致性原则。要保证整个软件设计过程中具有统一的规范、统一的标准和统一的文件模式等。

(5)明确性原则。每个模块必须功能明确、接口明确,消除多重功能和无用接口。

(6)模块之间的耦合尽可能小,模块的内聚度尽可能高。

(7)模块的扇入系数和扇出系数要合理。一个模块直接调用其他模块的个数称为模块的扇出系数;反之,一个模块被其他模块调用时,直接调用它的模块个数称为模块的扇入系数。模块的扇入、扇出系数必须适当。经验表明,一个设计得好的系统的平均扇入、扇出系数通常是3或4,一般不应超过7,否则会引起出错概率的增大。但菜单调用型模块的扇入与扇出系数可以大一些,公用模块的扇入系数可以大一些。

(8)模块的规模适当。过大的模块常常使系统分解得不充分,其内部可能包含了若干部分的功能,因此有必要进一步把原有的模块分解成若干功能尽可能单一的模块。但分解也必须适度,因为过小的模块有可能降低模块的独立性,造成系统接口的复杂性。

2. 子系统划分

1)子系统划分的原则

为了便于今后的系统开发和系统运行,子系统的划分应遵循以下几点原则。

(1)子系统要具有相对独立性。子系统的划分,必须使得子系统的内部功能、信息等各方面的凝聚性较好。子系统独立可以减少子系统间的相互影响,有利于多人分工开发不同的模块,从而提高软件产品的生产率,保证软件产品的质量,同时也增强了系统的可维护性和适应性。

(2)子系统之间数据的依赖性尽量小。子系统之间的联系要尽量减少,接口要简单明确。一个内部联系强的子系统对外部的联系必然很少,所以在划分的时候,应将联系较多者列入子系统内部,而剩余的一些分散、跨度比较大的联系,就成为这些子系统间的联系和接口。这样划分的子系统,将来调试、维护和运行都是非常方便的。

(3)子系统划分的结果应使数据冗余较小。如果把相关的功能数据分布到各个不同的子系统中,则会有大量的原始数据需要调用,大量的中间结果需要保存和传递,大量的计算工作将要重复进行,从而使得程序结构紊乱,数据冗余,不仅给编码带来很大的困难,而且系统的工作效率也大大降低。

(4)子系统的设置应考虑今后管理发展的需要。子系统的设置仅依靠上述系统分析的结构是不够的,因为现存的系统由于各种原因,很可能没有考虑到一些高层次管理决策的要求。

(5)子系统的划分应便于系统分阶段实现。信息系统的开发是一项较大的工程,它的实现一般要分批进行,所以子系统的划分应能适应这种分期分批的实施。另外,子系统的划分还必须兼顾组织结构的要求。

(6)子系统的划分应考虑到各类资源的充分利用。一个适当的子系统划分应该既考虑有利于各种设备资源在开发过程中的搭配使用,又考虑到各类信息资源的合理分布和充分使用,以减少系统对网络资源的过分依赖,减少输入、输出和通信等设备压力。

2)子系统结构设计

子系统结构设计的任务是确定划分后的子系统模块结构,并画出模块结构图。在这个过程中必须考虑以下几个问题。

(1)每个子系统如何划分成多个模块。

(2)如何确定子系统之间、模块之间传送的数据及其调用关系。

(3)如何评价并改进模块结构的质量。

(4)如何从数据流图导出模块结构图。

3. 系统模块结构设计

1)模块的概念

模块是组成系统的基本单位,它的特点是可以组合、分解和更换。系统中的任何一个处理功能都可以看成是一个模块。根据功能具体化程度的不同,模块可以分为逻辑模块和物理模块。在系统逻辑模型中定义的处理功能可视为逻辑模块。物理模块是逻辑模块的具体化,可以是一个计算机程序、子程序或若干条程序语句,也可以是人工过程的某项具体工作。

一个模块应具备以下4个要素。

(1)输入和输出。模块的输入来源和输出去向都是同一个调用者,即一个模块从调用者那里取得输入,进行加工后再把输出返回给调用者。

(2)处理功能。指模块把输入转换成输出所做的工作。

(3)内部数据。指仅供该模块本身引用的数据。

(4)程序代码。指用来实现模块功能的程序。

前两个要素是模块外部特性,反映了模块的外貌。后两个要素是模块的内部特性。在结构化设计中,主要考虑的是模块的外部特性,对其内部特性只做必要了解,具体的实现将在系统实施阶段完成。

2)模块结构图

为了保证系统设计工作的顺利进行,结构设计应遵循以下原则。

(1)所划分的模块其内部的凝聚性要强,模块之间的联系要少,即模块具有较强的独立性。

(2)模块之间的连接只能存在上下级之间的调用关系,不能有同级之间的横向联系。

(3)整个系统呈树状结构,不允许网状结构或交叉调用关系出现。

(4)所有模块(包括后继 IPO 图)都必须严格地分类编码并建立归档文件。

模块结构图主要关心的是模块的外部属性,即上下级模块、同级模块之间的数据传递和调用关系,并不关心模块的内部。

模块结构图是结构化设计中描述系统结构的图形工具。作为一种文档,它必须严格地定义模块的名字、功能和接口,同时还应当在模块结构图上反映出结构化设计的思想。模块结构图由模块、调用、数据、控制信息和转接符号5种基本符号组成,如图6-4所示,说明如下。

  • 模块。这里所说的模块通常是指用一个名字就可以调用的一段程序语句。在长方形中间标上能反映模块处理功能的模块名字。
  • 调用。箭头总是由调用模块指向被调用模块,但是应该理解被调用模块执行后又返回到调用模块。

如果一个模块是否调用一个从属模块,取决于调用模块内部的判断条件,则该调用模块间的判断调用采用菱形符号表示。如果一个模块通过其内部的循环功能来循环调用一个或多个从属模块,则该调用称为循环调用,用弧形箭头表示。判断调用和循环调用的表示方法如图6-5所示。

  • 数据。当一个模块调用另一个模块时,调用模块可以把数据传送到被调用模块供处理,而被调用模块又可以将处理的结构送回到被调用模块。在模块之间传送的数据,使用与调用箭头平行的带空心圆的箭头表示,并在旁边标上数据名。图6-6(a)表示模块 A 调用模块 B 时,A 将数据 $x$、$y$ 传送给 B,B 将处理结果数据 $z$ 返回给 A。
  • 控制信息。在模块间有时必须传送某些控制信息。例如,数据输入完成后给出的结束标志,文件读到末尾时所产生的文件结束标志等。控制信息与数据的主要区别是前者只反映数据的某种状态,不必进行处理。图6-6(b)中的”无此职工”就是用来表示送来的职工号有误的控制信息。
  • 转接符号。当模块结构图在一张纸上画不下,需要转接到另一张纸上,或者为了避免图上线条交叉时,都可以使用转接符号,圆圈内加上标号,如图6-7所示。

6.1.4 系统文档

信息系统的文档是系统建设过程的”痕迹”,是系统维护人员的指南,是开发人员与用户交流的工具。规范的文档意味着系统是按照工程化开发的,意味着信息系统的质量有了形式上的保障。文档的欠缺、文档的随意性和文档的不规范,极有可能导致原来的开发人员流动以后,系统不可维护、不可升级,变成了一个没有扩展性、没有生命力的系统。

信息系统的文档不仅包括应用软件开发过程中产生的文档,还包括硬件采购和网络设计中形成的文档;不仅包括上述有一定格式要求的规范文档,也包括系统建设过程中的各种来往文件、会议纪要、会计单据等资料形成的不规范文档,后者是建设各方谈判甚至索赔的重要依据;不仅包括系统实施记录,也包括程序资料和培训教程等。

对文档在系统开发人员、项目管理人员、系统维护人员、系统评价人员以及用户之间的多种作用总结如下。

(1)用户与系统分析人员在系统规划和系统分析阶段通过文档进行沟通。这里的文档主要包括可行性研究报告、总体规划报告、系统开发合同和系统方案说明书等。有了文档,用户就能依次对系统分析师是否正确理解了系统的需求进行评价,如不正确,可以在已有文档的基础上进行修正。

(2)系统开发人员与项目管理人员通过文档在项目期内进行沟通。这里的文档主要有系统开发计划(包括工作任务分解表、PERT 图、甘特图和预算分配表等)、系统开发月报以及系统开发总结报告等项目管理文件。有了这些文档,不同阶段之间的开发人员就可以进行工作的顺利交接,同时还能降低因为人员流动带来的风险,因为接替人员可以根据文档理解前面人员的设计思路或开发思路。

(3)系统测试人员与系统开发人员通过文档进行沟通。系统测试人员可以根据系统方案说明书、系统开发合同、系统设计说明书和测试计划等文档对系统开发人员所开发的系统进行测试。系统测试人员再将评估结果撰写成系统测试报告。

(4)系统开发人员与用户在系统运行期间进行沟通。用户通过系统开发人员撰写的文档运行系统。这里的文档主要是用户手册和操作指南。

(5)系统开发人员与系统维护人员通过文档进行沟通。这里的文档主要有系统设计说明书和系统开发总结报告。有的开发总结报告写得很详细,分为研制报告、技术报告和技术手册3个文档,其中的技术手册记录了系统开发过程中的各种主要技术细节。这样,即使系统维护人员不是原来的开发人员,也可以在这些文档的基础上进行系统的维护与升级。

(6)用户与维修人员在运行维护期间进行沟通。用户在使用信息系统的过程中,将运行过程中的问题进行记载,形成系统运行报告和维护修改建议。系统维护人员根据维护修改建议以及系统开发人员留下的技术手册等文档对系统进行维护和升级。

6.2 结构化分析方法

结构化分析与设计方法是一种面向数据流的传统软件开发方法,它以数据流为中心构建软件的分析模型和设计模型。结构化分析(Structured Analysis,SA)、结构化设计(Structured Design,SD)和结构化程序设计(Structured Programming Design,SPD)构成了完整的结构化方法。

6.2.1 结构化分析方法概述

抽象和分解是处理任何复杂问题的两个基本手段。

抽象是指忽略一个问题中与当前目标无关的那些方面,以便更充分地关注与当前目标有关的方面。对于一个复杂的问题,人们很难一下子考虑问题的所有方面和全部细节,通常可以把一个大问题分解成若干个小问题,将每个小问题再分解成若干个更小的问题,经过多次逐层分解,每个最底层的问题都是足够简单、容易解决的,于是复杂的问题也就迎刃而解了。这个过程就是分解的过程。

结构化方法就是采用这种自顶向下逐层分解的思想进行分析建模的。自顶向下逐层分解充分体现了分解和抽象的原则。随着分解层次的增加,抽象的级别也越来越低,即越来越接近问题的解。自顶向下的过程是分解的过程,自底向上的过程是抽象的过程。

结构化方法的分析结果由以下几部分组成:一套分层的数据流图、一本数据字典、一组小说明(也称加工逻辑说明)、补充材料。

6.2.2 数据流图

数据流图也称数据流程图(Data Flow Diagram,DFD),它是一种便于用户理解、分析系统数据流程的图形工具。它摆脱了系统的物理内容,精确地在逻辑上描述系统的功能、输入、输出和数据存储等,是系统逻辑模型的重要组成部分。

1. 数据流图的基本图形元素

数据流图中的基本图形元素包括数据流(Data Flow)、加工(Process)、数据存储(Data Store)和外部实体(External Agent)。其中,数据流、加工和数据存储用于构建软件系统内部的数据处理模型;外部实体表示存在于系统之外的对象,用来帮助用户理解系统数据的来源和去向。DFD 的基本图形元素如图6-8所示。

1)数据流

数据流由一组固定成分的数据组成,表示数据的流向。在 DFD 中,数据流的流向可以有以下几种:从一个加工流向另一个加工;从加工流向数据存储(写);从数据存储流向加工(读);从外部实体流向加工(输入);从加工流向外部实体(输出)。

DFD 中的每个数据流用一个定义明确的名字表示。除了流向数据存储或从数据存储流出的数据流不必命名外,每个数据流都必须有一个合适的名字,以反映该数据流的含义。

值得注意的是,DFD 中描述的是数据流,而不是控制流。

数据流或者由具体的数据属性(也称为数据结构)构成,或者由其他数据流构成。组合数据流是由其他数据流构成的数据流,它们用于在高层的数据流图中组合相似的数据流,以使数据流图更便于阅读。

2)加工

加工描述了输入数据流到输出数据流之间的变换,也就是输入数据流经过什么处理后变成了输出数据流。每个加工都有一个名字和编号。编号能反映出该加工位于分层 DFD 中的哪个层次和哪张图中,也能够看出它是哪个加工分解出来的子加工。

一个加工可以有多个输入数据流和多个输出数据流,但至少有一个输入数据流和一个输出数据流。数据流图中常见的3种错误如图6-9所示。

加工3.1.2有输入但是没有输出,我们称之为”黑洞”。因为数据输入到过程,然后就消失了。在大多数情况下,建模人员只是忘了输出。

加工3.1.3有输出但没有输入。在这种情况下,输入流似乎被忘记了。

加工3.1.1中输入不足以产生输出,我们称之为”灰洞”。这有几种可能的原因:一个错误的命名过程;错误命名的输入或输出;不完全的事实。灰洞是最常见的错误,也是最使人为难的错误。一旦数据流图交给了程序员,到一个加工的输入数据流必须足以产生输出数据流。

3)数据存储

数据存储用来存储数据。通常,一个流入加工的数据流经过加工处理后就消失了,而它的某些数据(或全部数据)可能被加工成输出数据流,流向其他加工或外部实体。除此之外,在软件系统中还常常要把某些信息保存下来以供以后使用,这时可以使用数据存储。例如,在考务处理系统中,报名时产生的考生名册要随着报名的过程不断补充,在统计成绩和制作考生通知书时还要使用考生名册的相关信息。因此,考生名册可以作为数据存储存在,以保存相关的考生信息。

每个数据存储都有一个定义明确的名字标识。可以有数据流流入数据存储,表示数据的写入操作;也可以有数据流从数据存储流出,表示数据的读操作;还可以用双向箭头的数据流指向数据存储,表示对数据的修改。

这里要说明的是,DFD 中的数据存储在具体实现时可以用文件系统实现,也可以用数据库系统实现。数据存储的存储介质可以是磁盘、磁带或其他存储介质。

4)外部实体(外部主体)

外部实体是指存在于软件系统之外的人员或组织,它指出系统所需数据的发源地(源)和系统所产生的数据的归宿地(宿)。例如,对于一个考务处理系统而言,考生向系统提供报名单(输入数据流),所以考生是考务处理系统的一个源;而考务处理系统要将考试成绩的统计分析表(输出数据流)传递给考试中心,所以考试中心是该系统的一个宿。

在许多系统中,某个源和某个宿可以是同一个人员或组织,此时,在 DFD 中可以用同一个符号表示。考生向系统提供报名单,而系统向考生送出准考证,所以在考务处理系统中,考生既是源又是宿。

源和宿采用相同的图形符号表示,当数据流从该符号流出时,表示它是源;当数据流流向该符号时,表示它是宿;当两者皆有时,表示它既是源又是宿。

2. 数据流图的扩充符号

在 DFD 中,一个加工可以有多个输入数据流和多个输出数据流,此时可以加上一些扩充符号来描述多个数据流之间的关系。

1)星号(*)

星号表示数据流之间存在”与”关系。如果是输入流则表示所有输入数据流全部到达后才能进行加工处理;如果是输出流则表示加工结束将同时产生所有的输出数据流。

2)加号(+)

加号表示数据流之间存在”或”关系。如果是输入流则表示其中任何一个输入数据流到达后就能进行加工处理;如果是输入流则表示加工处理的结果是至少产生其中一个输出数据流。

3)异或($\oplus$)

异或表示数据流之间存在”互斥”关系。如果是输入流则表示当且仅当其中一个输入流到达后才能进行加工处理;如果是输出流则表示加工处理的结果是仅产生这些输出数据流中的一个。

3. 数据流图的层次结构

从原理上讲,只要纸足够大,一个软件系统的分析模型就可以画在一张纸上。然而,一个复杂的软件系统可能涉及上百个加工和上百个数据流,甚至更多。如果将它们画在一张图上,则会十分复杂,不易阅读,也不易理解。

根据自顶向下逐层分解的思想,可以将数据流图按照层次结构来绘制,每张图中的加工个数可大致控制在”7加减2”的范围内,从而构成一套分层数据流图。

1)层次结构

分层数据流图的顶层只有一张图,其中只有一个加工,代表整个软件系统,该加工描述了软件系统与外界之间的数据流,称为顶层图。

顶层图中的加工(即系统)经分解后的图称为0层图,也只有一张。处于分层数据流图最底层的图称为底层图,在底层图中,所有的加工不再进行分解。分层数据流图中的其他图称为中间层,其中至少有一个加工(也可以是所有加工)被分解成一张子图。在整套分层数据流图中,凡是不再分解成子图的加工称为基本加工。

2)图和加工的编号

首先介绍父图和子图的概念。

如果某图(记为 $A$)中的某一个加工分解成一张子图(记为 $B$),则称 $A$ 是 $B$ 的父图,$B$ 是 $A$ 的子图。若父图中有 $n$ 个加工,则它可以有 $0\sim n$ 张子图,但每张子图只对应一张父图。

为了方便对图进行管理和查找,可以采用下列方式对 DFD 中的图和加工编号。

① 顶层图中只有一个加工(代表整个软件系统),该加工不必编号。

② 0层图中的加工编号分别为1、2、3…。

③ 子图号就是父图中被分解的加工号。

④ 对于子图中加工的编号,若父图中的加工号为 $x$ 的加工分解成某一子图,则该子图中的加工编号分别为 $x.1$、$x.2$、$x.3$…。

4. 分层数据流图的画法

下面以某考务处理系统为例介绍分层数据流图的画法。

考务处理系统的功能需求如下。

① 对考生送来的报名单进行检查。

② 对合格的报名单编好准考证号后将准考证送给考生,并将汇总后的考生名单送给阅卷站。

③ 对阅卷站送来的成绩清单进行检查,并根据考试中心指定的合格标准审定合格者。

④ 制作考生通知单(内含成绩合格/不合格标志)送给考生。

⑤ 按地区、年龄、文化程度、职业和考试级别等进行成绩分类统计和试题难度分析,产生统计分析表。

部分数据流的组成如下。

报名单=地区+序号+姓名+文化程度+职业+考试级别+通信地址\text{报名单} = \text{地区} + \text{序号} + \text{姓名} + \text{文化程度} + \text{职业} + \text{考试级别} + \text{通信地址}

正式报名单=准考证号+报名单\text{正式报名单} = \text{准考证号} + \text{报名单}

准考证=地区+序号+姓名+准考证号+考试级别+考场\text{准考证} = \text{地区} + \text{序号} + \text{姓名} + \text{准考证号} + \text{考试级别} + \text{考场}

考生名单=准考证号+考试级别\text{考生名单} = {\text{准考证号} + \text{考试级别}}(其中,{w}表示 w 重复多次)

考生名册=正式报名单\text{考生名册} = \text{正式报名单}

统计分析表=分类统计表+难度分析表\text{统计分析表} = \text{分类统计表} + \text{难度分析表}

考生通知单=准考证号+姓名+通信地址+考试级别+考试成绩+合格标志\text{考生通知单} = \text{准考证号} + \text{姓名} + \text{通信地址} + \text{考试级别} + \text{考试成绩} + \text{合格标志}

下面介绍画分层数据流图的步骤。

1)画系统的输入和输出

系统的输入和输出用顶层图来描述,即描述系统从哪些外部实体接收数据流,以及系统发送数据流到哪些外部实体。

顶层图只有一个加工,即待开发的软件系统。顶层图中的数据流就是系统的输入/输出信息。顶层图中通常没有数据存储。考务处理系统的顶层图如图6-10(a)所示。

2)画系统的内部

将顶层图的加工分解成若干个加工,并用数据流将这些加工连接起来,使得顶层图中的输入数据经过若干个加工处理后变换成顶层图的输出数据流,这张图称为0层图。从一个加工画出一张数据流图的过程实际上就是对这个加工的分解。

(1)确定加工。这里的加工指的是父图中某加工分解而成的子加工,可以采用下面两种方法来确定加工。

① 根据功能分解来确定加工。一个加工实际上反映了系统的一种功能,根据功能分解的原理,可以将一个复杂的功能分解成若干个较小的功能,每个较小的功能就是分解后的子加工。这种方法多应用于高层 DFD 中加工的分解。

② 根据业务处理流程确定加工。分析父图中待分解的加工的业务处理流程,流程中的每一步都可能是一个子加工。特别要注意在业务流程中数据流发生变化或数据流的值发生变化的地方,应该存在一个加工,该加工将原数据流(作为该加工的输入数据流)处理成变化后的数据流(作为该加工的输出数据流)。该方法较多应用于低层 DFD 中加工的分解,它能描述父加工中输入数据流到输出数据流之间的加工细节。

(2)确定数据流。当用户把若干个数据看作一个整体来处理(这些数据一起到达,一起加工)时,可以把这些数据看成一个数据流。通常,实际工作环境中的表单就是一种数据流。

在父图中某加工分解而成的子图中,父图中相应加工的输入/输出数据流就是子图边界上的输入/输出数据流。另外,在分解后的子加工之间应增添一些新的数据流,这些数据流是加工过程中的中间数据(对某子加工输入数据流的改变),它们与所有的子加工一起完成了父图中相应加工的输入数据流到输出数据流的变换。如果某些中间数据需要保存,以备使用,那么可以表示为流向数据存储的数据流。

同一个源或加工可以有多个数据流流向另一个加工,如果它们不是一起到达和一起加工的,那么可以将它们分成多个数据流。同样,同一个加工也可以有多个数据流流向另一个加工或宿。

(3)确定数据存储。在由父图中某加工分解而成的子图中,如果父图中该加工存在流向数据存储的数据流(写操作),或者存在从数据存储流向该加工的数据流(读操作),则这种数据存储和相关的数据流都画在子图中。

在分解的子图中,如果需要保存某些中间数据,以备以后使用,那么可以将这些数据组成一个新的文件。在自顶向下画分层数据流图时,新数据存储(首次出现的)至少应有一个加工为其写入记录,同时至少存在另一个加工读取该数据存储的记录。

注意,对于从父图中继承下来的数据存储,在子图中可能只对其读记录,或者写记录。

(4)确定源和宿。通常在0层图和其他子图中不必画出源和宿,有时为了提供可读性,可以将顶层图中的源和宿画在0层图中。

当同一个外部实体(人或组织)既是系统的源,又是系统的宿时,可以用同一个图形符号来表示。为了画图的方便,避免图中线的交叉,同一个源或宿可以重复画在 DFD 的不同位置,以增加可读性,但它们仍代表同一个实体。

在考务处理系统的0层图中,采用功能分解方法来确定加工。分析系统的需求说明,可知系统的功能主要分为考试报名及统计成绩两大部分,其中,报名工作在考试前进行,统计成绩工作在考试后进行。

为此,定义两个加工:登记报名表和统计成绩。0层图中的数据流,除了继承顶层图中的输入/输出数据流外,还应定义这两个加工之间的数据流。由于这两个加工分别在考试前后进行,并不存在直接关系,因此,”登记报名表”所产生的结果”考生名册”应作为数据存储,以便考试后由”统计成绩”读取。考务处理系统的0层图如图6-10(b)所示。

3)画加工的内部

当 DFD 中存在某个比较复杂的加工时,可以将它分解成一张 DFD 子图。分解的方法是将该加工看作一个小系统,该加工的输入/输出数据流就是这个假设的小系统的输入/输出数据流,然后采用画0层图的方法画出该加工的子图。

下面介绍考务处理系统0层图中加工1的分解,这里根据业务处理流程来确定加工1的分解。分析考务处理系统的功能需求和0层图,将加工1分解成3个子加工:检查报名表、编准考证号和登记考生。加工1分解而成的子图如图6-10(c)所示。

采用同样的方法画出加工2分解的 DFD 子图,如图6-10(d)所示。

重复第3)步的分解,直到图中尚未分解的加工都足够简单(也就是说,这种加工不必再分解)。

这里假设图6-10(c)、(d)中的每个加工都已经足够简单,不需要再分解,该考务处理系统的分层 DFD 绘制工作结束。

5. 分层数据流图的审查

在分层数据流图画好后,应该认真检查图中是否存在错误或不合理(不理想)的部分。

1)分层数据流图的一致性和完整性

分层数据流图的一致性是指分层 DFD 中不存在矛盾和冲突。这里讲的完整性是指分层 DFD 本身的完整性,即是否有遗漏的数据流、加工等元素。所以,分层 DFD 的一致性和完整性实际上反映了图本身的正确性。但是图本身的正确性并不意味着分析模型的正确性,分析模型的正确性要根据模型是否满足用户的需求来判断。

(1)分层数据流图的一致性。

① 父图与子图的平衡。父图与子图平衡是指任何一张 DFD 子图边界上的输入/输出数据流必须与其父图中对应加工的输入/输出数据流保持一致。

由于一张子图是被分解的加工的一种细化,所以,这张子图应该保证可以画到父图中替代被分解的加工,因此保持父图与子图平衡是理所当然的。

例如,图6-11所示的父图与子图是不平衡的。图6-11(b)是父图中加工2的子图,加工2的输入数据流有 $M$ 和 $N$,输出数据流是 $T$,而子图边界上的输入数据流是 $N$,输出数据流是 $S$ 和 $T$,很显然它们是不一致的。

如果父图中某个加工的一条数据流对应于子图中的几条数据流,而子图中组成这些数据流的数据项全体正好等于父图中的这条数据流,那么它们仍然是平衡的。

保持父图与子图平衡是画数据流的重要原则。自顶向下逐层分解是降低问题复杂性的有效途径。然而,如果只分别关注单张图的合理性,忽略父图与子图之间的关系,则很容易造成父图与子图不平衡的错误。

② 数据守恒。数据守恒包括两种情况:第一种情况是指一个加工的所有输出数据流中的数据必须能从该加工的输入数据流中直接获得,或者能通过该加工的处理而产生。

第二种情况是加工未使用其输入数据流中的某些数据项。这表明这些未用到的数据项是多余的,可以从输入数据流中删去。当然,这不一定就是错误,只表示存在一些无用数据。然而这些无用的数据常常隐含着一些潜在的错误,如加工的功能描述不完整、遗漏或不完整的输出数据流等。因此,在检查数据守恒时,不应该忽视对这种情况的检查。

③ 局部数据存储。这里讨论分层数据流图中的一个数据存储应该画在哪些 DFD 中,不应该画在哪些 DFD 中。

在一套完整的分层 DFD 中,任何一个数据存储都应有写和读的数据流,否则这个文件就没有存在的必要。除非这个数据存储的建立是为另一个软件系统使用或者这个数据存储是由另一个软件系统产生和维护的。

在自顶向下分解加工的过程中,如果某个加工需要保存一些数据,同时在将加工的同一张 DFD 上至少存在另一个加工需要读这些数据,那么该数据存储应该在这张 DFD 上画出。也就是在一张 DFD 中,当一个数据存储作为多个加工之间的交界面时,该数据存储应该画出。如果在一张 DFD 中,一个数据存储仅与一个加工进行读/写操作,并且在该 DFD 的父(祖先)图中未出现过该数据存储,那么该数据存储只是相应加工的内部文件,在这张 DFD 中不应该画出。

④ 一个加工的输出数据流不能与该加工的输入数据流同名。同一个加工的输出数据流和输入数据流,即使它们的组成成分相同,仍应该给它们取不同的名字,以表示它们是不同的数据流。但是允许一个加工有两个相同的数据流分别流向两个不同的加工。

(2)分层数据流图的完整性。

① 每个加工至少有一个输入数据流和一个输出数据流。一个没有输入数据流或者没有输出数据流的加工通常是没有意义的。当出现这种情况时,常常意味着可能遗漏了某些输入数据流或输出数据流。

② 在整套分层数据流图中,每个数据存储应至少有一个加工对其进行读操作,另一个加工对其进行写操作。对于某一张 DFD 来说,可以只写不读或只读不写。

③ 分层数据流图中的每个数据流和文件都必须命名(除了流入或流出数据存储的数据流),并保持与数据字典一致。

④ 分层数据流图中的每个基本加工都应有一个加工规约。

2)构造分层 DFD 时需要注意的问题

(1)适当命名。DFD 中的每个数据流、加工、数据存储、外部实体都应被适当地命名,名字应符合被命名对象的实际含义。通常,数据流名可用名词或形容词加名词来描述;加工名可以用动词或及物动词加宾语来描述;数据存储名可以用名词来描述;外部实体可以用实际的人员身份或组织的名称来命名。

用户在命名时应注意以下问题。

① 名字应反映整个对象(如数据流、加工),而不是只反映它的某一部分。

② 避免使用空洞的、含义不清的名字,如”数据””信息””处理””统计”等。

③ 如果发现某个数据流或加工难以命名,往往是 DFD 分解不当的征兆,此时应考虑重新分解。

(2)画数据流而不是控制流。数据流图强调的是数据流,而不是控制流。在 DFD 中一般不能明显地看出其执行的次序。为了区分数据流和控制流,可以简单地回答下列问题:”这条线上是否有数据流过?”,如果有表示是数据流,否则是控制流。

(3)避免一个加工有过多的数据流。当一个加工有过多的数据流时,意味着这个加工特别复杂,这往往是分解不合理的表现。解决的办法是重新分解,步骤如下。

① 把需要重新分解的某张图的所有子图连接成一张图。

② 把连接后的图重新划分成几个部分,使各部分之间的联系最小。

③ 重新定义父图,即第②步中的每个部分作为父图中的一个加工。

④ 重新建立各子图,即第②步中的每个部分都是一张子图。

⑤ 为所有的加工重新命名并编号。

(4)分解尽可能均匀。理想的分解是将一个问题(加工)分解成大小均匀的若干个子问题(子加工),也就是说,对于任何一张 DFD,其中的任何两个加工的分解层数之差不超过1。如果在同一张图中,某些加工已是基本加工,而另一些加工仍需分解若干层,那么,这张图就是分解不均匀的。

(5)先考虑确定状态,忽略琐碎的细节。在构造 DFD 时,应集中精力先考虑稳定状态下的各种问题,暂时不考虑系统如何自动、如何结束、出错处理以及性能等问题,这些问题可以在分析阶段的后期,在需求规约中加以说明。

(6)随时准备重画。对于一个复杂的软件系统,其分层 DFD 很难一次开发成功,往往要经历反复多次的重画和修改,才能构造出完整、合理、满足用户需求的分层 DFD。

3)分解的程度

在自顶向下画数据流图时,为了便于对分解层数进行把握,可以参照以下几条与分解有关的原则。

(1)7加减2。

(2)分解应自然,概念上应合理、清晰。

(3)只要不影响 DFD 的易理解性,可适当增加子加工数量,以减少层数。

(4)一般来说,上层分解得快一些(即多分解几个加工),下层分解得慢一些(即少分解几个加工)。

(5)分解要均匀。

6.2.3 数据字典(DD)

数据流图描述了系统的分解,但没有对图中各成分进行说明。数据字典就是为数据流图中的每个数据流、文件、加工,以及组成数据流或文件的数据项做出说明。其中,对加工的描述称为”小说明”,也可以称为”加工逻辑说明”。

1. 数据字典的内容

数据字典有以下4类条目:数据流、数据项、数据存储和基本加工。数据项是组成数据流和数据存储的最小元素。源点、终点不在系统之内,故一般不在字典中说明。

(1)数据流条目。数据流条目给出了 DFD 中数据流的定义,通常列出该数据流的各组成数据项。在定义数据流或数据存储组成时,使用表6-1给出的符号。

符号 含义 举例及说明
= 被定义为
+ 与 $x=a+b$,表示 $x$ 由 $a$ 和 $b$ 组成
$[\cdots\vert\cdots]$ 或 $x=[a\vert b]$,表示 $x$ 由 $a$ 或 $b$ 组成
${\cdots}$ 重复 $x={a}$,表示 $x$ 由0个或多个 $a$ 组成
$m{\cdots}n$ 或 ${\cdots}_m^n$ 重复 $x=2{a}5$ 或 $x={a}_2^5$,表示 $x$ 中最少出现2次 $a$,最多出现5次 $a$。5、2为重复次数的上、下限
$(\cdots)$ 可选 $x=(a)$ 表示 $a$ 可在 $x$ 中出现,也可不出现
“…” 基本数据元素 $x=”a”$,表示 $x$ 是取值为字符 $a$ 的数据元素
$\cdot\cdot$ 连接符 $x=1..9$,表示 $x$ 可取 $1\sim9$ 中的任意一个值

(2)数据存储条目。数据存储条目是对数据存储的定义。

(3)数据项条目。数据项条目是不可再分解的数据单位。

(4)基本加工条目。加工条目是用来说明 DFD 中基本加工的处理逻辑的,由于下层的基本加工是由上层的加工分解而来,只要有了基本加工的说明,就可理解其他加工。

2. 数据词典管理

词典管理主要是把词典条目按照某种格式组织后存储在词典中,并提供排序、查找和统计等功能。如果数据流条目包含了来源和去向,文件条目包含了读文件和写文件,还可以检查数据词典与数据流图的一致性。

3. 加工逻辑的描述

加工逻辑也称为”小说明”。常用的加工逻辑描述方法有结构化语言、判定表和判定树3种。

1)结构化语言

结构化语言(如结构化英语)是一种介于自然语言和形式化语言之间的半形式化语言,是自然语言的一个受限子集。

结构化语言没有严格的语法,它的结构通常可分为内层和外层。外层有严格的语法,内层的语法比较灵活,可以接近于自然语言的描述。

(1)外层。用来描述控制结构,采用顺序、选择和重复3种基本结构。

① 顺序结构。一组祈使语句、选择语句、重复语句的顺序排列。祈使语句是指至少包含一个动词及一个名词,指出要执行的动作及接受动作的对象。

② 选择结构。一般用 IF-THEN-ELSE-ENDIF、CASE-OF-ENDCASE 等关键词。

③ 重复结构。一般用 DO-WHILE-ENDDO、REPEAT-UNTIL 等关键词。

(2)内层。一般采用祈使语句的自然语言短语,使用数据字典中的名词和有限的自定义词,其动词含义要具体,尽量不用形容词和副词来修饰,还可使用一些简单的算法运算和逻辑运算符号。

2)判定表

在有些情况下,数据流图中某个加工的一组动作依赖于多个逻辑条件的取值。这时,用自然语言或结构化语言都不易于清楚地描述出来,而用判定表能够清楚地表示复杂的条件组合与应做的动作之间的对应关系。

判定表由4个部分组成,用双线分割成4个区域,如图6-12所示。

3)判定树

判定树是判定表的变形,一般情况下它比判定表更直观,且易于理解和使用。

6.3 结构化设计方法

结构化设计(Structured Design,SD)方法是一种面向数据流的设计方法,它可以与 SA 方法衔接。结构化设计方法的基本思想是将系统设计成由相对独立、功能单一的模块组成的结构。

结构化设计方法中用结构图(Structure Chart)来描述软件系统的体系结构,指出一个软件系统由哪些模块组成,以及模块之间的调用关系。

6.3.1 结构化设计的步骤

结构化设计大致可以分为两步进行,第一步是建立一个满足软件需求规约的初始结构图,第二步是对结构图进行改进。

1. 建立初始结构图

结构化方法本质上是一种功能分解方法。在结构化设计时,可以将整个软件看作一个大的功能模块(结构图中的模块),通过功能分解将其分解成若干个较小的功能模块,每个较小的功能模块还可以进一步分解,直到得到一组不必再分解的模块(结构图中的底层模块)。当一个功能模块分解成若干个子功能模块时,该功能模块实际上就是根据业务流程调用相应的子功能模块,并根据其功能要求对子功能的结果进行处理,最终实现其功能要求。

功能模块的分解应满足自顶向下、逐步求精、信息隐蔽、高内聚低耦合等设计准则,模块的大小应适中。通常,一个模块的大小以50~100行程序代码为宜,即一个模块的程序代码可以写在1~2页纸上。

2. 对结构图的改进

初始结构图往往存在一些不合理的设计(包括不合理的模块分解),因此,可根据设计准则对其进行改进。

3. 书写设计文档

在概要设计完成之后应书写设计规格说明,特别要为每个模块书写模块的功能、接口、约束和限制等,必要时可建立模块开发卷宗。

4. 设计评审

对设计结果及文档进行评审。

6.3.2 数据流图到软件体系结构的映射

结构化设计是将结构化分析的结果(数据流图)映射成软件的体系结构(结构图)。根据信息流的特点,可将数据流图分为变换型数据流图和事务型数据流图,其对应的映射分别称为变换分析和事务分析。

1. 信息流的类型

在需求分析阶段,用 SA 方法产生了数据流图。面向数据流的设计能方便地将 DFD 转换成程序结构图。DFD 中从系统的输入数据流到系统的输出数据流的一连串连续变换形成了一条信息流。DFD 的信息流大体上可以分为两种类型:变换流和事务流。

(1)变换流。信息沿着输入通路进入系统,同时将信息的外部形式转换成内部表示,然后通过变换中心(也称主加工)处理,再沿着输出通路转换成外部形式离开系统。具有这种特性的信息流称为变换流。变换流型的 DFD 可以明显地分成输入、变换(主加工)和输出三大部分。

(2)事务流。信息沿着输入通路到达一个事务中心,事务中心根据输入信息(即事务)的类型在若干个动作序列(称为活动流)中选择一个来执行,这种信息流称为事务流。事务流有明显的事务中心,各活动流以事务中心为起点呈辐射状流出。

2. 变换分析

变换分析是从变换流型的 DFD 导出程序结构图。

1)确定输入流和输出流,分离出变换中心

把 DFD 中系统输入端的数据流称为物理输入,系统输出端的数据流称为物理输出。物理输入通常要经过编辑、格式转换、合法性检查、预处理等辅助性的加工才能为主加工的真正输入(称为逻辑输入)。从物理输入端开始,一步步向系统的中间移动,可找到离物理输入端最远,但仍可被看作系统输入的那个数据流,这个数据流就是逻辑输入。同样,由主加工产生的输出(称为逻辑输出)通常也要经过编辑、格式转换、组成物理块、缓冲处理等辅助加工才能变成物理输出。从物理输出端开始,一步步向系统的中间移动,可找到离物理输出端最远,但仍可被看作系统输出的那个数据流,这个数据流就是逻辑输出。

DFD 中从物理输入到逻辑输入的部分构成系统的输入流,从逻辑输出到物理输出的部分构成系统的输出流,位于输入流和输出流之间的部分就是变换中心。

2)第一级分解

第一级分解主要是设计模块结构的顶层和第一层。一个变换流型的 DFD 可以映射成如图6-13所示的程序结构图。图中顶层模块的功能就是整个系统的功能。输入控制模块用来接收所有的输入数据,变换控制模块用来实现输入到输出的变换,输出控制模块用来产生所有的输出数据。

3)第二级分解

第二级分解主要是设计中、下层模块。

(1)输入控制模块的分解。从变换中心的边界开始,沿着每条输入通路,把输入通路上的每个加工映射成输入控制模块的一个低层模块。

(2)输出控制模块的分解。从变换中心的边界开始,沿着每条输出通路,把输出通路上的每个加工映射成输出控制模块的一个低层模块。

(3)变换控制模块的分解。变换控制模块通常没有通用的分解方法,应根据 DFD 中变换部分的实际情况进行设计。

4)事务分析

事务分析是从事务流型 DFD 导出程序结构图。

(1)确定事务中心和每条活动流的流特性。图6-14给出了事务流型 DFD 的一般形式。其中,事务中心(图中的 T)位于数条活动流的起点,这些活动流从该点呈辐射状流出。每条活动流也是一条信息流,它可以是变换流,也可以是另一条事务流。一个事务流型的 DFD 由输入流、事务中心和若干条活动流组成。

(2)将事务流型 DFD 映射成高层的程序结构。事务流型 DFD 的高层结构如图6-15所示。顶层模块的功能就是整个系统的功能。接收模块用来接收输入数据,它对应于输入流。发送模块是一个调度模块,控制下层的所有活动模块。每个活动流模块对应于一条活动流,它也是该活动流映射成的程序结构图中的顶层模块。

(3)进一步分解。接收模块的分解类同于变换分析中输入控制模块的分解。每个活动流模块根据其流特性(变换流或事务流)进一步采用变换分析或事务分析进行分解。

5)SD 方法的设计步骤

(1)复查并精化数据流图。

(2)确定 DFD 的信息流类型(变换流或事务流)。

(3)根据流类型分别实施变换分析或事务分析。

(4)根据系统设计的原则(参见5.1.2节)对程序结构图进行优化。

6.4 WebApp 分析与设计

WWW 的早期(大约从1990年到1995年),Web 站点仅包含链接在一起的一些超文本文件,这些文件使用文本和有限的图形来表示信息。随着时间的推移,一些开发工具(例如 XML、Java)扩展了 HTML 的能力,使得 Web 工程师在向客户提供信息的同时也能提供计算能力。因此,基于 Web 的系统与应用(总称为 WebApp)诞生了。今天,WebApp 已经发展成为成熟的计算工具,这些工具不仅可以为最终用户提供独立的功能,而且已经同公司数据库和业务应用集成在一起了。WebApp 的特性使得大多数 WebApp 适合采用敏捷开发过程模型进行开发。

6.4.1 WebApp 的特性

绝大多数 WebApp 具备下列属性。

(1)网络密集性。WebApp 驻留在网络上,服务于不同客户全体的需求。网络提供开放的访问和通信(如 Internet)或者受限的访问和通信(如企业内联网)。

(2)并发性。大量用户可能同时访问 WebApp。很多情况下最终用户的使用模式存在很大的差异。

(3)无法预知的负载量。WebApp 的用户数量每天都可能有数量级的变化。例如,周一显示有100个用户使用系统,周四就有可能会有10000个用户。

(4)性能。如果一位 WebApp 用户必须等待很长时间(访问、服务器端处理、客户端格式化显示),该用户就可能转向其他地方。

(5)可用性。尽管期望百分之百的可用性是不切实际的,但是对于热门的 WebApp,用户通常要求能够24/7/365(全天候)访问。

(6)数据驱动。许多 WebApp 的主要功能是使用超媒体向最终用户提供文本、图片、音频及视频内容。除此之外,WebApp 还常被用来访问那些存储在 Web 应用环境之外的数据库中的信息。

6.4.2 WebApp 需求模型

WebApp 分析与传统的软件需求分析有所不同。虽然传统的软件需求分析的概念和原则全部可以应用于 Web 工程的分析活动,但 WebApp 分析的内容及参考的元素还是呈现出了其特殊性。并针对其特点,出现如面向数据的基于 ER 发展而来的 WebML、模型对象基于 UML 的 UWE 等建模方法。严格意义上讲,这些方法并非均为结构化方法,但是为了集中描述 WebApp 的建模,将其归在本章介绍。

建模以需求工程中确定的用户类别、可用目标、使用场景、业务环节等各类需求等为输入,产生如下5种主要的模型类型。

1. 内容模型

内容模型给出由 WebApp 提供的全部系列内容,包括文字、图形、图像、音频和视频。内容模型包含结构元素,它们为 WebApp 的内容需求提供了一个重要的视图。这些结构元素包含内容对象和所有分析类,在用户与 WebApp 交互时生成并操作用户可见的实体。

内容的开发可能发生在 WebApp 实现之前、WebApp 构建之中,或者 WebApp 投入运行以后的很长一段时间里。在每种情况下,它都通过导航链接合并到 WebApp 的总体结构中。内容对象可能是产品的文本描述、描述新闻事件的一篇文章、拍摄的一张动作照片、在一次论坛讨论中某个用户的回答、一个演讲的简短视频等。这些内容对象可能存储分布于分离的文件中,直接嵌入 Web 页中,或从数据库中动态获得。换句话说,内容对象是呈现给最终用户具有汇聚信息的所有条目。

通过检查直接或间接引用内容的场景描述,可以根据用例直接确定出内容对象。内容模型必须具备描述内容对象构件的能力。在许多实例中,用一个简单的内容对象列表,并给出每个对象的简短描述就足以定义和实现所必需的内容需求。但是在某些情况下,内容模型可以从更丰富的分析中获得好处,即在内容对象之间的关系和 WebApp 维护的内容层次中采用图形表示其关系。

图6-16中的数据树表述了常用于描述某个构件的一种信息层次,不带阴影的长方形表示简单或复合数据项(一个或多个数据值),带阴影的长方形表示内容对象。在此图中,”描述说明”是由5个内容对象定义的。在某些情况下,随着数据数的扩展,其中的一个或多个对象将会得到进一步细化。

由多项内容对象和数据项组成的任何内容都可以生成数据树。开发数据树尽力在内容对象中定义层级关系,并提供一种审核内容的方法,以便在开始设计前发现遗漏和不一致的内容。另外,数据树可以作为内容设计的基础。

2. 交互模型

交互模型描述了用户与 WebApp 采用了哪种交互方式。绝大多数 WebApp 都能使最终用户与应用系统的功能、内容及行为之间进行”会话”。交互模型由一种或多种元素构成,包括用例、顺序图、状态图、用户界面原型等。

(1)用例是交互分析的主要工具,方便客户理解系统的功能。在许多实例中,一套用例足以描述在分析阶段的所有交互活动。然而,当遇到复杂的交互顺序并包含多个分析类或多任务时,有时更值得采用严格图解方式描述它们。

(2)顺序图是交互分析中描述用户与系统进行交互的方式。通过顺序图,能显示用户按照一定的顺序使用系统,以完成相应的功能。

(3)状态图是交互分析中对系统进行动态的描述。

(4)用户界面原型展现用户界面布局、内容、主要导航链接、实施的交互机制及用户 WebApp 的整体美观度。尽管用户界面原型的设计可以说是一个设计活动,但最好在创建分析模型时就实施它。对用户界面的物理表示评估得越早,越有可能满足最终用户的需求。

3. 功能模型

许多 WebApp 提供大量的计算和操作功能,这些功能与内容直接相关(既能使用又能生成内容)。这些功能常常以用户——WebApp 的交互活动为主要目标,正是由于这个原因必须对功能需求进行分析,并且当需要时也可以用于建模过程。

功能模型定义了将用于 WebApp 内容并描述其他处理功能的操作,这些处理功能不依赖于内容却是最终用户所必需的。功能模型描述 WebApp 的两个处理元素,每个处理元素代表抽象过程的不同层次:①用户可观察到的功能是由 WebApp 传递给最终用户的;②分析类中的操作实现与类相关的行为。

用户可观察到的功能包括直接由用户启动的任何处理功能。这些功能实际上可能使用分析类中的操作完成,但是从最终用户的角度看,这些功能是可见的结果。

在抽象过程的更低层次,分析模型描述了由分析类操作执行的处理。这些操作可以操纵类的属性,并参与类之间的协作来完成所需要的行为。不管抽象过程的层次如何,UML 的活动图可以用来表示处理细节。在分析阶段,仅在功能相对复杂的地方才会使用活动图。许多 WebApp 的复杂性不是出现在提供的功能中,而是与可访问信息的性质以及操作的方式相关。

4. 导航模型

导航模型为 WebApp 定义所有导航策略。导航模型考虑了每一类用户如何从一个 WebApp 元素(例如内容对象)导航到另一个元素。把导航机制定义为设计的一部分,在这个阶段,分析人员应该关注于总体的导航需求,应考虑以下问题。

  • 某些元素比其他元素更容易达到吗(即需要更少的导航步骤)?表示的优先级是什么?
  • 为了促使用户以他们自己的方向导航,应该强调某些元素吗?
  • 应该怎样处理导航错误?
  • 导航到关键元素组的优先级应该高于导航到某个特定元素的优先级吗?
  • 应该通过链接方式、基于搜索的访问方式还是其他方式来实现导航?
  • 根据前面的导航行为,某些确定的元素应该展现给用户吗?
  • 应该为用户维护导航日志吗?
  • 在用户交互的每一点处,一个完整的导航地图或菜单都可用吗?
  • 导航设计应该由大多数普遍期望的用户行为来驱动,还是由已定义的 WebApp 元素可感知的重要性来驱动?
  • 为了促进将来使用快捷方式,一个用户能否”存储”他以前对 WebApp 的导航?
  • 应该为哪类用户设计最佳导航?
  • 应该如何处理 WebApp 外部的链接?应该覆盖现有的浏览器窗口吗?能否作为一个新的浏览器窗口,还是作为一个单独的框架?

提出并回答这些问题及其他一些问题是导航分析的一部分工作。

5. 配置模型

配置模型描述 WebApp 所在的环境和基础设施。

在某些情况下,配置模型只不过是服务器端和客户端的属性列表。但是对于更复杂的 WebApp 来说,多种配置的复杂性(例如,多服务器之间的负载分配、高速缓存的体系结构、远程数据库、同一网页上服务于不同对象的多个服务器)可能对分析和设计产生影响。在必须考虑复杂配置体系结构的情况下,可以使用 UML 部署图。

6.4.3 WebApp 设计

好的 WebApp 应该具有的最相关的通用特性是可用性、功能性、可靠性、效率、可维护性、安全性、可扩展性、以及及时性。WebApp 的设计目标是简单性、一致性、符合性、健壮性、导航性、视觉吸引力与兼容性。WebApp 设计根据其类型不同,适合采用混合的各种技术,进行一系列设计动作,包括:架构设计、构件设计、内容设计、导航设计、美学设计、界面设计。很多情况下,多类设计并行进行。

1. 架构设计

WebApp 描述了使 WebApp 达到其业务目标的基础结构,典型使用多层架构来构造,包括用户界面或展示层、基于一组业务规则来指导与客户端浏览器进行信息交互的控制器,以及可以包含 WebApp 的业务规则的内容层或模型层,描述将以什么方式来管理用户交互、操作内部处理任务、实现导航及展示内容。模型-视图-控制器(Model-View-Controller,MVC)结构是 WebApp 基础结构模型之一,它将 WebApp 功能及信息内容分离。

2. 构件设计

在 WebApp 中,内容和功能的界限通常并不清晰,因此首先明确 WebApp 构件:①定义良好的聚合功能,为最终用户处理内容或提供计算或处理数据;②内容和功能的聚合包,提供最终用户所需要的功能。因此,WebApp 构件设计通常包括内容设计元素和功能设计元素。

(1)构件级内容设计。关注内容对象,以及包装后展示给最终用户的方式,应该适合创建的 WebApp 特性。一般情况下,内容对象不需要被组织成构件,加以分别实现。但是,随着 WebApp、内容对象及其关系的规模和复杂度的增长,组织内容是必要的。

(2)构件级功能设计。将 WebApp 作为一系列构件加以交付,这些构件与信息体系结构并行开发,以确保一致性。并且需要在一开始就考虑需求模型和初始信息架构,然后再进一步考查功能如何影响用户与系统的交互、要展示的信息以及要管理的用户任务。在架构设计中,往往将 WebApp 的内容和功能结合在一起来设计功能架构,描述关键的功能构件及其交互。

3. 内容设计

WebApp 的内容结构(线性或非线性)也影响架构,因此设计内容体系结构。内容体系结构着重于内容对象(诸如网页的组成对象)的表现和导航的组织,通常采用线性结构、网格结构、层次结构、网络结构四种结构及其组合。当内部交互可预测顺序很常见时,采用线性结构,如帮助文档、产品订单输入顺序等。当 WebApp 内容按类别组织成十分规则的二维或多维时,可以采用网格结构,如销售网站水平代表要出售的商品准类,垂直表示制造商。层次结构可以设计成是控制流水平地穿过垂直分支(通过超文本)的方式,即同层之间可以通过超链接相连。网络结构一般是对网页进行设计,使其能够将控制通过超链接传递到系统中几乎所有的网页。将上述设计结构进行组合,就形成复合结构。

4. 导航设计

建立了 WebApp 的体系结构及其构件后,定义导航路径,使用户可以访问 WebApp 的内容和功能。为每一类用户角色定义一组类,包含一个或多个内容对象或者包含 WebApp 的功能。当用户与 WebApp 进行交互时,会接触到一系列导航语义单元,即信息(导航节点)和相关的导航结构(导航链接)的集合,它们相互协作共同完成相关的用户请求的一部分。定义导航机制,如导航链接,水平或垂直导航条(列表),标签或者一个完整的站点地图入口。

用户界面是围绕着运行在客户端上的浏览器的特性进行设计的,数据层位于服务器上。

6.5 用户界面设计

用户界面(UI)设计在人与计算机之间搭建了一个有效的交流媒介。遵循一系列的界面设计原则定义界面对象和界面设计动作,然后创建构成用户界面原型基础的屏幕布局。

6.5.1 用户界面设计的黄金原则

Theo Mandel 在其关于界面设计的著作中提出了3条”黄金原则”:用户操纵控制;减少用户的记忆负担;保持界面一致。

这些黄金原则实际上构成了一系列用户界面设计原则的基础,这些原则可以指导软件设计的重要方面。

1. 用户操纵控制

在很多情况下,设计者为了简化界面的实现可能会引入约束和限制,其结果可能是界面易于构建,但会妨碍使用。Mandel 定义了一组设计原则,允许用户操纵控制。

1)以不强迫用户进入不必要的或不希望的动作的方式来定义交互模式

交互模式就是界面的当前状态。例如,如果在字处理软件的菜单中选择拼写检查,则软件将转移到拼写检查模式。如果用户希望在这种情形下进行一些文本编辑,则没有理由强迫用户停留在拼写检查模式,用户应该能够几乎不需要做任何动作就能进入和退出该模式。

2)提供灵活的交互

由于不同的用户有不同的交互偏好,因此应该提供选择机会。例如,软件可能允许用户通过键盘命令、鼠标移动、数字笔、多触摸屏或语音识别命令等方式进行交互。但是,每个动作并非要受控于每一种交互机制。例如,考虑使用键盘命令(或语音输入)来画一幅复杂形状的图形是有一定难度的。

3)允许中断和撤销用户交互

即使当陷入到一系列动作之中时,用户也应该能够中断动作序列去做某些其他事情(而不会失去已经做过的工作),用户也应该能够”撤销”任何动作。

4)当技能级别增长时可以使交互流线化并允许定制交互

用户经常发现他们重复地完成相同的交互序列。因此,值得设计一种”宏”机制,使得高级用户能够定制界面以方便交互。

5)使用户与内部技术细节隔离开来

用户界面应该能够将用户移入到应用的虚拟世界中来。用户不应该知道操作系统、文件管理功能或其他神秘的计算技术。其实,界面不应该要求用户在机器内部层次上进行交互(例如,不应该要求用户在应用软件中输入操作系统命令)。

6)设计应允许用户与出现在屏幕上的对象直接交互

当用户能够操纵完成某任务所必需的对象,并且以一种该对象好像是真实物理存在的方式来操作它时,用户就会有一种控制感。例如,某应用界面可允许用户”拉伸”某对象(增大其尺寸),就是直接操纵的一种实现。

2. 减轻用户的记忆负担

用户必须记住的东西越多,与系统交互时,出错的可能性也就越大。因此,一个经过精心设计的用户界面不会加重用户的记忆负担。只要有可能,系统应该”记住”有关的信息,并通过能够帮助回忆的交互场景来帮助用户。Mandel 定义了一组设计原则,使得界面能够减少用户的记忆负担。

1)减少对短期记忆的要求

当用户陷于复杂的任务时,短期记忆的要求将会很大。界面的设计应该尽量不要求记住过去的动作、输入和结果。可行的解决办法是通过提供可视的提示,使得用户能够识别过去的动作,而不是必须记住它们。

2)建立有意义的默认

初始的默认集合应该对一般的用户有意义,但是,用户应该能够说明个人的偏好。然而,”reset(重置)”选择应该是可用的,使得可以重新定义初始默认值。

3)定义直观的快捷方法

当使用助记符来完成系统功能时,助记符应该以容易记忆的方式联系到相关动作。

4)界面的视觉布局应该基于真实世界的象征

例如,一个账单支付系统应该使用支票簿和支票登记簿来指导用户的账单支付过程。这使得用户能够依赖于很好理解的可视提示,而不是记住复杂难懂的交互序列。

5)以不断进展的方式揭示信息

界面应该以层次化的方式进行组织,即关于某任务、对象或某行为的信息应该首先在高抽象层次上呈现。更多的细节应该在用户用鼠标点击表明兴趣后再展示。例如,很多字处理应用中十分常见的一个功能是加下画线,该功能本身是”文本风格”菜单下多个功能中的一个。然而,每种加下画线的功能并未列出,用户必须选择加下画线,然后所有加下画线的选项(如加单下画线、加双下画线、加虚下画线)才被展示出来。

3. 保持界面一致

用户应该以一致的方式展示和获取信息,这意味着:按照贯穿所有屏幕显示的设计规则来组织可视信息;将输入机制约束到有限的集合,在整个应用系统中得到一致的使用;从任务到任务的导航机制要一致地定义和实现。Mandel 定义了一组帮助保持界面一致性的设计原则。

1)允许用户将当前任务放入有意义的环境中

很多界面使用数十个屏幕图像来实现复杂的交互层次,提供指示器(例如,窗口标题、图标、一致的颜色编码)帮助用户知道当前工作环境是十分重要的。另外,用户应该能够确定它来自何处以及存在什么途径转换到新任务。

2)在应用系统家族内保持一致性

一组应用系统(或一套产品)应使用相同的设计规则,以保持所有交互的一致性。

3)如果过去的交互模型已经建立起了用户期望,除非有不得已的理由,否则不要改变它

一个特殊的交互序列一旦已经变成事实上的标准,则用户在遇到的每个应用系统中均会如此期望,如果改变,将导致混乱。

6.5.2 用户界面的分析与设计

用户界面的分析与设计过程始于创建不同的系统功能模型(从外部看对系统的感觉),用于完成系统功能的任务被分为面向人的和面向计算机的,考虑那些应用到界面设计中的各种设计问题,各种工具被用于建造原型并最终实现设计模型,最后由最终用户从质量的角度对结果进行评估。

1. 用户界面分析和设计模型

(1)软件工程师所创建的设计模型(Design Model)。整个系统设计模型包括对软件的数据结构、体系结构、界面和过程的表示。界面设计往往是设计模型的附带结果。

(2)人机界面设计工程师创建的用户模型(User Model)。用户模型描述系统最终用户的特点。在设计前,应首先对用户进行分类,了解用户的特点,包括年龄、性别、实际能力、教育、文化和种族背景、动机、目的以及个性等。

(3)最终用户在脑海里对界面产生的映像,称为用户的心理模型或系统感觉(System Perception)。系统感觉是最终用户主观想象的系统映像,描述了期望的系统能提供的操作,其描述的精确程度依赖于最终用户对软件的熟悉程度。

(4)系统实现者创建的系统映像(System Image)。系统映像包括基于计算机的系统的外在表示(界面的观感)和用来描述系统语法和意义的支撑信息(书、手册、录像带、帮助文件等)。如果系统映像和系统感觉是一致的,用户就会感觉软件很舒服,使用起来很有效。

为了融合这些模型,设计模型必须适应包含在用户模型中的信息,并且系统映像必须准确地反映接口的语法和语义。

2. 用户界面分析和设计的过程

用户界面的分析与设计过程是迭代的,包括 4 个不同的框架活动:界面分析及建模、界面设计、界面构造和界面确认。

界面分析活动的重点在于那些与系统交互的用户的轮廓,记录技能级别、业务理解以及对新系统的一般感悟,并定义不同的用户类型,对每个用户类别进行需求引导。

界面设计的目标是定义一组界面对象和动作(以及它们的屏幕表示),使得用户能够以满足系统所定义的每个使用目标的方式完成所有定义的任务。

界面构造通常开始于创建可评估使用场景的原型。随着迭代设计过程的继续,用户界面开发工具可以用来完成界面的构造。

界面确认着重于以下几点:界面正确地实现每个用户任务的能力、适应所有任务变化的能力以及达到所有一般用户需求的能力;界面容易使用和学习的程度;用户将界面作为其工作中有用工具的接受程度。

6.5.3 用户界面设计问题

在进行用户界面设计时,几乎总会遇到以下 4 个问题:系统响应时间、帮助设施、错误信息处理、菜单和命令标记。

1. 系统响应时间

系统响应时间不能令人满意是交互式系统用户经常抱怨的问题。一般来说,系统响应时间是指从用户开始执行动作到软件以预期的输出和动作形式给出响应这段时间。

系统响应时间包括两方面的属性:时间长度和可变性。如果系统响应时间过长,用户就会感到焦虑和沮丧。系统时间的可变性是指相对于平均时间的偏差,在很多情况下这是最重要的响应时间特性。即使响应时间比较长,响应时间的低可变性也有助于用户建立稳定的交互节奏。例如,稳定在 $1\,\text{s}$ 的命令响应时间,比从 $0.1\,\text{s}$ 到 $2.5\,\text{s}$ 不定的响应时间要好。

2. 帮助设施

几乎所有计算机交互式系统的用户都时常需要帮助,考虑帮助设施时需要在设计中解决以下问题。

(1)进行系统交互时,是否在任何时候对任何系统功能都能得到帮助?有两种选择:提供部分功能与动作的帮助和提供全部功能的帮助。

(2)用户怎样请求帮助?有 3 种选择:帮助菜单、特殊功能键和 HELP 命令。

(3)如何表达帮助?有 3 种选择:提供单独的帮助窗口、在另一个窗口中指示参考某个已印刷的文档(不是理想方式)或在屏幕特定位置给出一行或两行的简单提示。

(4)用户如何回到正常的交互方式?可做的选择包括屏幕上显示的返回按钮、功能键或控制序列。

(5)如何构造帮助信息?有 3 种选择:平面结构(所有信息均通过关键词来访问)、分层结构(用户可以进入分层结构得到更详细的信息)和超文本的使用。

3. 错误信息处理

出错信息和警告是指出现问题时系统反馈给用户的“坏消息”。如果做不好,出错信息和警告会给出无用和误导的信息,反而增加了用户的沮丧感。通常,交互式系统给出的出错信息和警告应具备以下特征。

(1)消息以用户可以理解的语言描述问题。

(2)消息应提供如何从错误中回复的建设性意见。

(3)消息应指出错误可能导致哪些不良后果(例如破坏数据文件),以便用户检查是否出现了这些情况。

(4)消息伴随着视觉或听觉上的提示。也就是说,显示消息应该伴随警告声或者消息用闪烁方式显示,或以明显表示错误的颜色来显示。

(5)消息不应是裁判性的,即不能指责用户。

4. 菜单和命令标记

输入命令曾经是用户和系统交互的主要方式,并广泛用于各种应用程序中。现在,面向窗口的界面采用点击和选取方式,减少了用户对输入命令的依赖。但许多高级用户仍然喜欢面向命令的交互方式。在提供命令或菜单标签交互方式时,必须考虑以下问题。

(1)每个菜单选择是否都有对应的命令?

(2)以何种方式提供命令?有 3 种选择:控制序列(如 Alt+P)、功能键或输入命令。

(3)学习和记忆命令的难度有多大?忘记命令怎么办?

(4)用户是否可以定制和缩写命令?

(5)在界面环境中菜单标签是不是自解释的?

(6)子菜单是否与主菜单所指的功能相一致?