详细设计阶段的根本目标是确定应该怎样具体地实现所要求的系统,即得出对目标系统的精确描述,从而在编码阶段可以把这个描述直接翻译成用某种程序语言书写的程序。为了表达更精确,所以需要很多的描述工具。
1.软件工程学概述:软件工程学概述-CSDN博客
2.软件过程深度解析:软件过程深度解析-CSDN博客
3.软件工程之需求分析涉及的图与工具:软件工程之需求分析涉及的图与工具-CSDN博客
4.软件工程之形式化说明技术深度解析:https://lzm07.blog.csdn.net/article/details/147803429
一、过程设计的工具
(一)程序流程图
程序流程图(Program Flowchart)是最直观的过程设计工具,通过图形符号表示程序的控制流程。其核心思想是将算法分解为顺序、选择、循环三种基本结构,用矩形、菱形等符号描述步骤和条件判断。
1.表示形式:
(1)起始 / 结束:椭圆表示程序的开始或结束。
(2)处理步骤:矩形表示赋值、计算等操作。
(3)输入 / 输出:平行四边形表示数据的输入或输出。
(4)决策:菱形表示条件判断,根据结果选择不同分支。
(5)流程线:箭头表示控制流方向。
2.实现过程:
(1)需求分析:明确程序的功能和逻辑流程(如用户登录验证)。
(2)结构分解:将算法分解为基本结构(如顺序执行输入、验证、输出结果)。
(3)符号映射:用流程图符号表示各步骤和条件(如验证用户名密码是否正确)。
(4)绘制优化:检查流程的完整性和逻辑正确性,避免死循环或冗余分支。
3.具体示例:用户登录验证流程
项目背景:某电商平台开发用户登录功能,需验证用户名和密码的正确性。
流程说明:
(1)输入用户名和密码:用户在登录界面输入信息。
(2)验证用户名:查询数据库判断用户名是否存在。
(3)验证密码:对输入密码进行 MD5 加密后与数据库存储的密码比对。
(4)结果输出:根据验证结果提示“登录成功”或“用户名 / 密码错误”。
程序流程图:
优缺点:
优点:直观易懂,适合描述简单流程。
缺点:缺乏模块化支持,易导致结构化破坏(如GOTO语句滥用)。
(二)盒图(N-S图)
盒图(Nassi-Shneiderman 图,N-S图)是一种符合结构化程序设计原则的图形工具,强制使用顺序、选择、循环三种基本结构,避免流程线的滥用。
1.表示形式:
(1)顺序结构:上下排列的矩形,表示顺序执行的步骤。
(2)选择结构:包含条件判断的矩形,分支用嵌套矩形表示。
(3)循环结构:包含循环条件和循环体的矩形,分为WHILE型和UNTIL型。
2.实现过程:
(1)确定控制结构:分析程序逻辑,确定使用的结构类型(如循环嵌套选择)。
(2)嵌套绘制:根据结构层次,在盒图中嵌套绘制子结构。
(3)标注条件:在选择和循环结构中明确标注条件表达式。
3.具体示例:计算阶乘(n!)
项目背景:开发一个计算阶乘的函数,输入整数 n,输出 1×2×…×n。
流程说明:
(1)输入 n:用户输入非负整数。
(2)初始化结果:设置 result=1。
(3)循环计算:从 1 到 n 依次乘以 result。
(4)输出结果:打印 result 的值。
优缺点:
优点:强制结构化设计,避免逻辑混乱。
缺点:层次嵌套过多时可读性下降。
(三)PAD图
PAD 图(Problem Analysis Diagram)是一种树形结构的过程设计工具,使用二维图形表示程序逻辑,支持自顶向下逐步细化。
1.表示形式:
(1)顺序结构:横向排列的矩形,表示顺序执行的步骤。
(2)选择结构:包含条件判断的矩形,分支用竖线分隔。
(3)循环结构:包含循环条件和循环体的矩形,分为WHILE型和UNTIL型。
2.实现过程:
(1)顶层设计:定义程序的整体目标和主要步骤。
(2)逐层细化:将每个步骤分解为更具体的子步骤,直到达到代码级。
(3)标注条件:在选择和循环结构中明确标注条件表达式。
以下是一个示例:
3.具体示例:冒泡排序算法
项目背景:实现一个数组排序函数,将数组元素按升序排列。
流程说明:
(1)输入数组:用户输入待排序数组。
(2)外层循环:从第一个元素到倒数第二个元素遍历。
(3)内层循环:比较相邻元素,若前一个大于后一个则交换。
(4)输出结果:打印排序后的数组。
优缺点:
优点:层次清晰,适合复杂算法的设计。
缺点:对循环嵌套的表示较为复杂。
以下是一个示例:
(四)判定表
判定表(Decision Table)是一种用于描述多条件组合下执行不同动作的工具,适合处理逻辑复杂的决策问题。
1.表示形式:
(1)条件桩:列出所有可能的输入条件。
(2)动作桩:列出所有可能的输出动作。
(3)条件项:表示条件桩的取值组合(T/F 或具体值)。
(4)动作项:表示对应条件组合下应执行的动作。
2.实现过程:
(1)识别条件和动作:分析问题,确定输入条件和输出动作。
(2)生成条件组合:列出所有可能的条件取值组合。
(3)填充动作项:为每个条件组合确定对应的动作。
(4)简化合并:合并等价的条件组合,减少冗余。
3.具体示例:订单折扣规则
项目背景:某电商平台根据订单金额和会员等级给予不同折扣。
(1)条件桩:
订单金额(A):A > 1000 元,A ≤ 1000 元。
会员等级(L):L = 高级,L = 普通。
(2)动作桩:
折扣率(D):D = 20%,D = 10%,D = 5%,无折扣。
(3)判定表:
条件桩 |
条件项 1 |
条件项 2 |
条件项 3 |
条件项 4 |
订单金额 A |
>1000 |
>1000 |
≤1000 |
≤1000 |
会员等级 L |
高级 |
普通 |
高级 |
普通 |
--------------- |
--------- |
--------- |
--------- |
--------- |
折扣率 D |
20% |
10% |
5% |
无 |
流程说明:
根据订单金额和会员等级查找对应的折扣率。
例如,若订单金额为1500元且会员为高级,则折扣率为20%。
优缺点:
优点:清晰展示多条件组合,避免逻辑遗漏。
缺点:当条件数量较多时,表格规模庞大。
(五)判定树
判定树(Decision Tree)是一种树形结构的决策工具,通过层次化的条件判断逐步确定最终动作。
1.表示形式:
(1)根节点:表示初始条件或问题。
(2)内部节点:表示条件判断,分支表示不同的条件取值。
(3)叶子节点:表示最终的动作或结果。
2.实现过程:
(1)确定决策顺序:按重要性或优先级排列条件。
(2)构建树结构:从根节点开始,逐层添加条件分支。
(3)标注动作:在叶子节点标注对应的动作。
3.具体示例:贷款审批流程
项目背景:某银行根据客户信用评分和收入情况决定是否批准贷款。
(1)条件顺序:
信用评分(S):S ≥ 700,S < 700。
收入(I):I ≥ 5000 元,I < 5000元。
(2)判定树:
贷款审批
┌─── S ≥ 700?
│ ┌─── I ≥ 5000 → 批准
│ └─── I < 5000 → 拒绝
└─── S < 700 → 拒绝
流程说明:
首先检查信用评分是否≥700。
若满足,进一步检查收入是否≥5000 元,决定是否批准。
若信用评分 < 700,直接拒绝。
优缺点:
优点:直观易懂,适合业务规则的可视化。
缺点:条件复杂时树结构过于庞大,维护困难。
二、面向数据结构的设计方法
(一)Jackson图
Jackson图是一种用于描述数据结构和程序结构的层次化工具,通过顺序、选择、重复三种基本结构表示数据元素的组成关系。
1.表示形式:
(1)顺序结构:用“→”表示元素按顺序出现。
(2)选择结构:用“S (i)”表示从多个元素中选择一个,i 为选择条件。注意下一个结构在右上角出现的小圆圈。
(3)重复结构:用“I (i)”表示元素重复出现,i 为循环条件。注意下一个结构在右上角出现的小星号。
2.实现过程:
(1)分析数据结构:识别输入输出数据的层次关系。
(2)绘制Jackson图:用顺序、选择、重复结构表示数据元素。
(3)映射程序结构:将数据结构转换为程序模块的层次结构。
3.具体示例:学生成绩统计系统
项目背景:某高校需统计学生各科成绩的平均分和总分。
(1)数据结构:
学生信息:学号、姓名、课程列表。
课程列表:多门课程,每门课程包含课程名、成绩。
(2)Jackson图:
学生信息
├─ 学号
├─ 姓名
└─ 课程列表(I(课程未结束))
├─ 课程名
└─ 成绩
(3)数据库映射:
学生表(学号,姓名)。
课程表(课程名,成绩)。
选课表(学号,课程名)。
(二)Jackson方法
Jackson方法是一种基于数据结构的程序设计方法,通过分析输入输出数据结构,推导出程序的控制结构。
1.实现步骤:
(1)数据结构分析:用Jackson图描述输入输出数据的层次结构。
(2)对应关系识别:找出输入输出数据结构中具有直接因果关系的数据单元。
(3)程序结构推导:根据数据结构的对应关系,构建程序的模块层次。
(4)操作分配:将具体操作分配到程序模块中。
2.具体示例:文件复制系统
项目背景:开发一个文件复制工具,将源文件内容复制到目标文件。
(1)数据结构:
输入文件:由多个字符组成的序列。
输出文件:与输入文件相同的字符序列。
(2)程序结构推导:
对应关系:输入文件的每个字符对应输出文件的一个字符。
(3)程序模块:
输入模块:读取源文件字符。
复制模块:将字符写入目标文件。
循环控制模块:判断文件是否结束。
java代码示例:
public class FileCopy {
public void copy(String source, String target) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(source));
BufferedWriter writer = new BufferedWriter(new FileWriter(target));
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
reader.close();
writer.close();
}}
优缺点:
优点:数据结构与程序结构紧密关联,易于维护。
缺点:对复杂数据结构的处理能力有限。
三、程序复杂度的定量度量
(一)McCabe方法
McCabe方法通过计算程序控制流图的环路复杂度(Cyclomatic Complexity)来衡量程序的复杂性,公式为:V(G) = E - N + 2P
其中,E为边数,N为节点数,P为强连通分量数(通常P=1)。
1.实现过程:
(1)绘制控制流图:将程序分解为基本块,用节点表示,边表示控制流。
(2)计算复杂度:代入公式计算环路复杂度。
2.具体示例:二分查找算法
java代码示例:
public int binarySearch(int[] arr, int target) {
int low = 0;
int high = arr.length - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;}
控制流图:
节点:6个(初始、循环条件、计算 mid、判断相等、调整 low、调整 high、结束)。
边:7条(循环条件的 T/F 分支、各步骤间的转移)。
复杂度计算:V(G) = 7 - 6 + 2×1 = 3
复杂度解读:复杂度为 3,表示该程序有3条独立路径,需至少3个测试用例覆盖所有路径。
(二)Halstead方法
Halstead方法通过统计程序中的操作符和操作数数量,计算程序的复杂度,公式包括:
(1)程序长度:N = N_1 + N_2,其中N_1为操作符总数,N_2为操作数总数。
(2)程序词汇量:n = n_1 + n_2,其中n_1为唯一操作符数,n_2为唯一操作数。
(3)程序容量:V = N×log_2 n。
1.实现过程:
识别操作符和操作数:遍历程序代码,统计所有操作符和操作数。
计算指标:代入公式计算复杂度指标。
2.具体示例:计算阶乘函数
Java代码示例:
public int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; i++) {
result *= i;
}
return result;}
操作符和操作数统计:
操作符:=, +=, <=, ++, return(共 5 种,n_1=5)。
操作数:result, i, n(共 3 种,n_2=3)。
操作符总数:N_1=5(=, *=, <=, ++, return)。
操作数总数:N_2=5(result, i, n, 1, 1)。
复杂度计算:n = 5 + 3 = 8 N = 5 + 5 = 10 V = 10×log_2 8 = 10×3 = 30
复杂度解读:程序容量为 30,表示该程序的信息量较大,可能需要更多的测试和维护。
四、结语
详细设计是将软件需求转化为可执行代码的关键环节,合理选择工具和方法可显著提升设计质量:
(1)过程设计工具:流程图适合简单流程,盒图和PAD图强制结构化设计,判定表和判定树处理复杂决策。
(2)面向数据结构方法:Jackson图和Jackson方法通过数据结构驱动程序设计,适合层次化数据处理。
(3)复杂度度量:McCabe方法关注控制流复杂度,Halstead方法量化代码的信息量。
实际项目中,需根据系统特性选择工具组合(如流程图 + Jackson图 + McCabe方法),并结合代码审查和测试确保设计的正确性。未来,随着AI辅助设计工具的发展,详细设计将更高效、智能化,进一步降低开发成本。