第2章
流程图
计算机领域很早就开始使用流程图(flowchart)了,这可能是使用最早的一类行为模型。在20世纪60年代,工具供应商通常会提供一些具有可塑性的流程图模板,程序员可以据此绘制出更整齐的流程图。IBM公司甚至提供了一个带有基本标记的不同规格标准的流程图模板。前辈们开玩笑说,这是该领域的第一个CASE(计算机辅助软件工程)工具。
2.1 定义与表示法
通常有两种不同风格的流程图标记形式。图2-1所示为第一种,这是一种极简风格的表示方式,只有表示动作、决策和两种页连接符的符号。分页连接符一般用于不能在一页上显示完全的大型系统中。传统的分页连接符使用大写字母ABC…,第一个分页连接符是A,与之对应的连接点也用A表示。
在流程图使用的早期,因为比较关心系统的输入/输出设备,所以开发出很多扩展的流程图符号,如图2-2所示,这里面甚至有表示卡片式I/O的符号。
在这种流程图标记方式中,“?流?”的部分使用带箭头的线段,表示从某个流程图符号开始,在某个流程图符号处结束。图2-3是特浓咖啡自动售卖机的流程图示例,利用这个咖啡机,可以用1欧元买到一小杯意大利特浓咖啡。后续我们还会使用这个案例讨论流程图技术。
2.2 技术详解
图2-3中的流程图能够处理自动售卖机的销售过程。接收欧元硬币,给出特浓咖啡。如果仔细观察流程图,可能会注意到,所有的箭头都会在符号的顶部结束。这不是强制要求,但这样做有助于理解。决策框只能有一个入口,既然是决策过程,那么至少应有两个出口箭头。在早先的Fortran年代,“Arithmetic IF”语句带有3个输出。这个钻石形状的框(决策符号框)的边与底部顶点可以分别表示3种选择(<、=、>)。在图2-3中很容易看到这3个选项。如果多于3个选项(比如Case/Switch语句),每个选项对应一个连接箭头即可。(标识符是为使用者服务的,不必非要遵守条条框框)。过程框则最多只有一个输出箭头。但它们可能有多个输入箭头。箭头上的标签通常表明决策框有几个可能的出口,Yes/No、True/False或者数值显示决策结果。从过程框发出的箭头则没有标签。这样表示过程框的过程是完整的,流程走向下一个框。框之间不会表达信息或内容。使用者需要自己确定过程框的结果对于后续框是否可用(流程图具有“记忆性”)。
对于过程框和决策框来说,框内文本内容几乎没有限制,可以有不同的形式,这些形式的抽象程度也可以不同。框内文本可以简明扼要,如图2-3所示。或者也可以非常具体,甚至可直接是某种编程语言。例如“硬币是”决策框的输出结果可以是1.00欧元、0.50欧元或0.20欧元三种形式。同样,决策条件也可以表示为针对每一种硬币类型的二进制条件,如“是”或“否”。不管使用哪种方式,最好要保持一致,如果不同抽象级别的文本表达混合在一起,那将是很让人困惑的。流程图的符号集可以支持结构化编程的3种基本结构:顺序、选择、循环。例如图2-3所示的决策框表示了“选择”结构,而图2-3底部还有一处表示了“循环”结构:如果变量“总金额”与1欧元的比较结果是“小于”,就会返回到投入硬币过程框。再看一下1欧元那个分支,它结束“提供特浓咖啡”这个过程框,这正是“顺序”结构的例子。在结构化编程中,通常有“单一入口,单一出口”的设计惯例,但是这个惯例并不是强制要求,图2-3所示例子是符合这个惯例的。
流程图采用分层策略来处理不同抽象级别的过程。也就是说,一个高抽象级别的过程框可以扩展成一系列更详细、单独的流程图。如果这样做,那么每一个低抽象级别的流程图都应该被命名,以此来清晰地表明它是从某个高抽象级别的过程框扩展而来的,或者干脆使用分页连接符(只是分页连接符的方式显得有些笨重)。本章结束部分的表2-4,总结了在流程图中可以表达的控制事件。
2.3 案例分析
2.3.1 日期计算函数
NextDate函数是测试圈里非常流行的一个例子,它非常简单,很容易找到测试用例的预期输出。(给定某个日期,NextDate函数返回下一个日期。)图2-4和图2-5采用分层流程图方式将功能进行了分解。变量lastDay的数值通过其他方式计算出来,然后与决策框中的日期进行比较。如果某个变量可以在多处赋值,那么必须保证最终计算出来的数值在运行过程中不会相互改写。在NextDate流程图中,有3个值被分配给nextDay、nextMonth和nextYear。决策框要求这3个赋值必须是互斥的。在这些计算过程中使用到了GoTo语句,当使用流程图来建立主要的行为模型时,使用GoTo语句是一种常用作法。在流程图中可以清晰地看到,NextDate函数的逻辑是很严密的。
2.3.2 风寒指数表
著名的风寒指数(密歇根州或者其他寒冷地区)是由两个变量构成的函数:每小时风速V和摄氏温度T。其公式如下所示:
其中,W是人脸的表面温度,以华氏度为单位;T是空气温度,以华氏度为单位,
-20≤T≤50;V是风速,以mile/h(1mile?=?1609.34m)为单位,3≤V≤73。
基于图2-6所示的流程图可以完成一个类似表2-1的表格,请注意其中的循环嵌套。
(例子中的数值范围是随意制定的,在密歇根州,这是真实情况!)风寒表格中温度的范围是-20℉≤T≤50℉,每次递增5℉;风速范围是3≤V≤73,每次递增5mile/h。
2.3.3 保费计算流程图
图2-7是1.8.1节中定义的保费计算问题的流程图模型。
2.3.4 车库门控系统流程图
图2-8是1.8.2节中定义问题的流程图模型。
2.4 基于流程图派生的测试用例
流程图中的路径可以直接推导出抽象的测试用例。由于流程图可以显示并发的路径,因此很容易手工设计出相关的抽象测试用例。从图2-7所示的保费计算流程图中可以看到关于年龄和保费变量的并发路径。毫无疑问,并发路径应该是互斥的。(测试用例也可以利用某个指定流程图的语义内容派生出来,但这反过来又要求手动推导测试用例生成。)通常来说,从流程图中不可能直接设计出具体的测试用例,因为流程图只显示了需要完成的过程,并不实际执行这些过程。
(对于类似Fortran这样的程序来说,流程图很好用。)而对于事件驱动型应用来说,流程图的形式就显得不太适合了。从接下来给出的事件驱动示例,可以清晰看出以下几个问题。
- 事件可以表示为决策的输出或者过程(过程框)。
- 由于没有专门的事件标识符,所以事件只能表示为流程图中有明确语义的内容。
- 需要具有洞察力和领域经验才能识别与上下文相关的输入事件。
2.4.1 保费计算问题的测试用例
图2-7所示的流程图中一共有40条不同的路径,其中36条对应着表2-2列出的使用等价类测试方法生成的测试用例。年龄和出险次数变量由变量的取值范围来定义,因此从表面上看,只需要测试某种形式的边界值就足够充分了。将每个等价类中的边界值映射到相同的乘法和加法函数集合中,它们会在结果中产生大量的冗余,因而这并无太大价值。图2-9显示了表2-2中测试用例1的路径。
表2-2显示了保费计算问题的抽象测试用例和具体测试用例。抽象测试用例可以从流程图中直接设计出来(MBT工具可以完成这个过程)。实际数值则需要从需求中获取。从图2-7中给定的文本可以看出,对于MBT工具来说,直接生成测试用例需要的实际数值还是很困难的。我主要使用电子表格中的替换功能来完成这部分工作,这样还不算太麻烦。通常来说,电子表格是MBT工具很顺手的一个补充。
下面是图2-8所示路径对应的测试用例1中的抽象和具体测试用例。
2.4.2 车库门控系统的测试用例
在车库门控系统的流程图中有两个循环:一个是停下并重启正在关闭的门,另一个是停下并重启正在打开的门。(一个是在关门过程中停下或者重启,一个是在开门过程中停下或者重启。)如果假设输入事件和输出过程是同时发生的,那么数学家会说,在图2-8所示的流程图中,存在一个含有不同路径的可数无穷路径集合。在实际生活中,我家的车库门需要大概13s关闭或者开启,而停止/重启序列需要大概1s,因此,实际上车库门流程图只有一个有限数目的可能路径。图2-10显示了其中一条路径,该路径由下面的测试用例来表示。图2-10所示的流程图符号是有编号的,以表示路径的追踪和命名。
表2-3包含车库门流程图中的5条不同路径。我们先简要描述一下每条路径(也可以视之为用户场景),然后再使用按系列编号的流程图符号来详细描述它们。5条路径涵盖了每个流程图符号和流程图中的每条边。另外还有些路径是针对光束被打断之后的过程的,这些路径也包括每个上下文的控制设备的输入事件。对于工具来说,很难从流程图中设计出详细的、类似使用用例(use case,从用户角度考虑的使用场景)的测试用例。
表2-4包括从图2-8所示流程图中设计出的手工测试用例信息。从中我们可以看出,流程图的定义存在一些“小问题”,原因如下:
1)输入事件看上去既可以是决策框的输出,也可以是过程框中表示的一个过程;
2)事件上下文(状态)看上去是与过程框对应的过程,也可以说是决策框的输出;
3)有两个潜在的死循环:正在开门或者关门时停止。
除非具有领域经验,否则,利用输入事件/输出过程对是没有办法定位下一步操作的。
2.5 优势与局限
流程图有很多优势。如果任何一种表达方式已经使用了数十年,那么它肯定还是有可取之处的。对于流程图来说,这个优势就很容易理解。由于过程框和决策框里面的文本可以使用自然语言,因此流程图使客户和开发者之间具有更好的交互性。就连美国的IRS都是用流程图来解释复杂的税务代码的。如我们之前所说,流程图能够表达基本的结构化编程架构。其中,还有没有明确说明的地方,这就是对于流程图中“存储器”功能的使用。如果一个变量在过程框中被赋予一个值,那么该变量在后续需要保持这个数值。在图2-3中可以清楚地看到,变量“总金额”被定义之后,在之后的循环中可以再次定义它。任何“形式化好的”流程图都可以使用命令式和结构化的编程语言进行编码。如示例中所示,流程图支持几种级别的抽象,因此它们具有可扩展性,能够描述大型而且复杂的应用。另一个优势是,它们可以用来描述复杂的计算和算法。最后一个优势是有些控制过程或者行为可以通过流程图中不同的路径来表达。
流程图也有些限制。由于流程图的本质是将过程序列化,所以很难表达事件驱动的系统,因为在事件驱动的系统里面,独立事件可能以任何顺序发生。此外,流程图很难描述被描述系统需要操作的外部设备的上下文。虽然面向设备的I/O符号可以完成这个工作,但是需要很多扩展。流程图几乎没有办法表达数据,除非是在过程框或者I/O框里面。过程框和决策框里面的文本可以包含变量名,但这是很粗浅的表达。数据表达都如此困难,表达数据结构以及数据之间的关系就更难了。同样,对于描述事件,它也有很多潜在的困难。从图2-8中可以看到,设备控制信号有时表示为过程框,有时又显示为决策框的输出。表2-5将流程图的描述能力与第1章定义的标准列表进行了对比。
2.6 经验教训
在20世纪60年代晚期,电话交换机系统开发实验室需要将所有的交互系统源代码的流程图文档提供给运营公司。当时,源代码大概是30万行的汇编程序。在这个过程中,软件工程师需要向图案部提交手绘流程图,六周以后,软件工程师就可以得到非常完美的流程图。在这六周内,如果设计师想要对流程图进行修改,那么他们可以将原始草图替换成修改之后的草图,以免在图案部里重新排队。
同时,我的管理团队中的一位数学家参加了一次研讨会,会上他看到一个程序,这个程序能够在CalComp绘图机上画出离散部件电路图。我们研究了技术资料后,决定将电路图符号替换成流程图符号,结果就产生了AELFLow系统[Jorgensen和Papendick 1970]。这是我们学到的第一个经验:为了使绘图机能够得到认可,我们向每个部门展示了他们如何从绘图机上获益。推销了几周之后,我们最终得到许可购买了最小的绘图机。6个月之后,我们有了最大的绘图机。AELFlow系统非常有效,不仅节约了返工的时间,而且提高了设计文档的整体可用性。相比汇编代码,流程图更容易从技术角度进行描述。从各方面来看,AELFlow系统都是真正的CASE工具,这比术语CASE的使用要早得多。
这里关键的教训是:变革是很难引入的,它需要时间、耐心和对企业的认知,同时也需要培训。尽管对于AELFLow系统来说,培训是很少的一部分。Gartner Hype的周期是相当精确的,尽管持续的间隔可能有变化(如图2-11所示)。准备引入MBT的组织一定要经历宣传周期。我的观点是,期望膨胀的峰值期源于MBT产品的销售团队,泡沫破裂的幻灭期则源于不同模式的不充分的培训和教育。稳步爬升的复苏期开始于合适的工具和良好的模式教育,实质生产的成熟期则随着市场的关注度和占有率而保持。