软件设计与架构笔记(5)
前文描述的HIPO模型是一个典型的基于结构图的IPO系统设计模型,其基本思想依然是由顶至下,逐步求精。基于经验Larry进一步总结了通用的系统设计准则[SMC74]。
程序结构和问题结构。减少程序变更所造成影响的重要方法之一,就是保证设计结构匹配问题本身的结构。由顶至下的思维模式会天然形成一种层级结构,因此重点在于如何决定设计单元在相同层级,或隶属于不同层级,而关键又在于理解问题本身。
模块控制范围和决策影响范围。控制范围指模块以及归属于该模块的子模块的集合;影响范围指某个设计决策所造成变更的所有模块集合。当设计决策的影响范围尽可能位于该决策所在的模块控制范围之内时,该系统设计就可以被认为是“简洁”的。保持简洁性的方法之一可以是提升某些决策相关的元素的层级;或者把受到相同决策影响,但位于不同控制结构的模块重新划分至相同控制范围。
模块大小。模块的实际大小可被用于描述潜在问题的信号。过小的模块可能缺少功能性绑定,而过大的模块可能涵盖了超过一个功能性绑定。前者可以通过inline的方式消除以减少模块规模,后者由于可理解性和可读性问题需要进行进一步拆分。
错误和文件终止处理。当模块的一部分功能需要通知其调用者发生某件错误时,可通过返回某种错误参数实现,该参数的值最好是二元类型,对于流数据处理的EOF标记也需要进行类似处理。同时这些参数也不应该包含如何处理当前错误的信息,而是由调用者决定。当然,如果模块本身不需要错误标记时,系统设计就更简洁了。
初始化。某些模块由于需要依赖初始化操作,从而可能存在“简洁”但导致“弱绑定”的设计。例如,读模块的access方法可能会遇到“文件未打开”的错误,如果选择将错误信息返回,调用者自然会选择调用open方法然后重新read;但另一种维护“黑盒性”的做法是,在access内部遇到该错误时自动通过open和reread进行恢复,那么调用者就不需要知道“文件未打开”这种错误并且重复进行处理了。
模块选择。消除重复的功能,而非消除重复的代码。如果只是通过抽取的方式简单消除重复代码,那么有可能导致某个变更造成更多的修改。一种识别该问题的方法是,关注那些被其它不同模块调用,以及调用其它不同模块的对象,判断是否存在其子功能与不同的模块集合关联的情况,如果是则意味着存在层级或模块缺失的可能。
隔离软件规格说明。软件设计规格的重要内容就是描述特定的数据类型、记录布局以及索引结构,设计应尽量使其与系统其他模块进行隔离,从而减少规格变更导致的重写。
参数数量。尽量减少模块间调用的参数数量(不只是个数),如果参数中存在一个完整的数据记录,应尽量只传递必要的数据记录,否则也会导致该记录的变更对模块造成潜在影响。
结构化分析(Structured Analysis)
随着软件设计方法论的发展和问题复杂度的增加,人们发现设计不再是解决复杂系统面临的唯一难题。比如,传统的软件设计过程一般是按由顶至下的方法,依照需求规格说明(requirement specification)给出具体的软件对象定义,那么如何构建规范合理的需求规格说明呢?另外,如果软件设计过程愈加复杂,是否可以按照经典的分治法(divide-and-conquer)对其进行分解和简化呢?
世界上存在多种多样的原始需求形态,例如采用文字叙述(narrative)可以说是最普遍的形式之一。当问题复杂度增加时,软件设计已经不能从简单的叙述中加以消化并诞生,于是就出现了需求分析的过程。这种把问题从原始形式转换成可进一步规范设计的规格说明的过程,被称为系统分析。结构化分析作为软件系统分析最早流行起来的方法论,是在早期工业界数十年的探索中发展起来的。
由于传统的文字叙述不足以表达复杂系统,人们开始重视并使用符号语言,例如德国数学家Carl Adam Petri发表于1962年的Petri Net。60年代中期,女数学家Erna Schneider Hoover在贝尔实验室领导了一支团队,其目标是分析电话交换机系统的性能和宕机时间,Erna使用了Petri Net来模拟复杂的电话交换系统。受此启发,同时困扰于晦涩难懂的叙述式规格说明的年轻工程师Tom DeMarco由此开始开发一套网络符号语言,由此发展并最终在1978年发表了结构化分析方法[TOM78]。
结构化分析与传统系统分析
Tom认为传统的系统分析包含如下目标:
确定最优化目标。
生成该目标的细节描述,并且能够被后期的实现过程用于评估该目标是否实现。
生成该目标相关的重要参数预测,包括花费、收益、日程以及性能特性。
得出所有被影响部分之上的项的并发性。
为了达成这些目标,系统分析活动需要涉及用户沟通、撰写规格说明、损耗收益研究、可行性分析以及估算等。然而,这些活动都因高复杂性存在很多问题。针对这些问题,结构化分析进一步拓展了系统分析的目标:
分析的产生物必须是可维护的,特别是针对目标文档(Target Document)。
必须采用有效的分割方法解决大小的问题,摒弃维多利亚小说式的规格说明。
尽可能使用图形表达。
必须区分逻辑和物理设计,并且基于此在分析师和用户之间合理分配职责。
必须在具体实现之前构建逻辑系统模型,使用户熟悉系统特性。
同时,结构化分析描述了一系列可被用于不同分析阶段的工具:数据流程图(Data Flow Diagram, DFD)、数据字典(Data Dictionary)以及逻辑策略表达工具,例如结构化英语(Structured English)、决策表(Decision Tables)以及决策树(Decision Trees)等。
数据流程图
DFD是一种描述相互关联的过程的网络,其作用是帮助分割需求,并在撰写规格说明之前记录这种分割。与普通流程图的区别是,DFD只聚焦在数据流动的过程,因此基本没有任何关于循环或逻辑决策的控制信息。为了举例说明DFD,[TOM78]描述了一个软件咨询公司的自动化管理和运营辅助系统,该系统的功能包含了学员注册、支付、人员管理、课程管理等方面。下图是对该公司的早期运营模型的描述:
该图是一种Logical DFD,图中的输入被称作事务(Transaction)。以其中一条主要路径的部分为例,该路径共描述了5种事务:Cancellations, Enrollments, Payments, Inqueries和Rejects(这里指不属于前4种类型的事务的统称),以及数据在这些事务间可能的流动关系。此外还有一种包含了系统具体实现信息的DFD,被称作Physical DFD。
DFD有时又被称作气泡图(Bubble Diagram),原因是其描述数据转换过程的符号——气泡。此外DFD还包含命名向量,用于表示数据路径;直线段,表示文件或数据库;矩形(或称为源/入节点),表示网络的起点或数据的接收者(通常是当前领域外的人或组织)。
DFD清晰地表达了工具的自然特征——如果DFD存在任何错误,也应当是显而易见、毋庸置疑的,这无疑减少了分析师与用户间产生认知分歧的可能。另一方面,实践证明DFD无论在概念描述或是建模方面都有显著价值。更重要的是,它提供了一种基于功能的系统分割方法,并且描述了不同部分之间的接口。在系统评审中,任何接口或过程的缺失都能够证明当前DFD的缺陷——这比纯粹的数学方式更加直观和有效。
在实际分析活动中通常使用分级数据流程图(Levelled DFD, LDFD)逐步求精分割系统功能。在LDFD中,通常存在3层、有时甚至更多层具有不同功能解析度的DFD。
Level 0,也被称为上下文图,通常仅包含一个气泡——也就是系统总的过程单位以及其它元素。这种图可以被用于和最宽泛的用户进行交流,例如干系人、业务分析员、数据分析员以及程序员。
Level 1,对上下文图的唯一气泡进行细分,将其分解成不同过程单位,以及相关的文件或数据库。
Level 2,进一步对Level 1进行划分,因此需要更多的文字和符号标记。
Level 3+,一般很少出现Level 3+的DFD,原因是这种级别的DFD可能存在过多的细节,从而导致难以沟通、比较和有效建模的问题。
数据字典
数据字典用于追踪和评估系统不同部分之前的接口,是对DFD的一种有效补充。以前面描述的系统DFD为例,过程3和7之间的数据流动Payment-Data,可以用如下公式进一步描述:
Payment-Data = Customer-Name +
Customer-Address +
Invoice-Number +
Amount-of-Payment
换句话说,Payment-Data包括了该公式右值的所有数据项,且这些数据项需依序且非空。更进一步,数据字典还可能需要对某些数据项进行进一步描述,例如Invoice-Number:
Invoice-Number = State-Code +
Customer-Account-Number +
Salesman-ID +
Sequential-Invoice-Count
与DFD类似,数据字典也是呈现了由顶至下的细分过程。每个DFD应该携带相应的数据字典描述,二者共同组成了系统分析的图形化产生物。
逻辑策略表达
逻辑策略表达用于替代传统冗长的文字叙述式的规格说明。最常见的结构化表达方式被称作结构化英语,例如采用按行缩进的方式表述不同层级的规格说明:
1 2 3 4 5 6 7 8 9 10 11 |
|
使用决策表表达上述规格说明,结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
决策树的表达结果如下:
结论
结构化设计为软件设计提供了有效的结构图工具,以及作者Larry富有经验的设计准则,至今仍极具指导意义。为了保证设计阶段能使用清晰有效的规格说明,结构化分析提供了强大的DFD分析工具和规格说明描述工具,尽管其核心依然是逐步求精的设计思想,但已经开始涉足于比编程活动更加宽泛的软件工业领域,最终形成了较为独立的需求工程,成为软件构建过程中不可或缺的环节。
引用
SMC74, Structured design