简介:这份停车场管理系统软件工程课程设计报告书面向计算机相关专业学生与软件工程课程设计者,围绕停车场车辆进出、车位调度与计费管理,完整呈现从需求分析到编码测试的课程设计过程。全套资料为 1 个 doc 文档,压缩包约 1.7MB,内含任务书、目录、章节正文与参考文献,可作为课程设计说明书的格式模板与交付参考。报告以 Windows XP 为平台,用 Microsoft VC++6.0 开发、Visio 2007 建模,正文覆盖系统制作背景、需求分析、概要结构与详细结构设计,并给出菜单函数、初始化模块、车辆进入与离开计费等关键实现思路,第五章测试分析还包含主菜单、初始化、进出场与收费的用例说明。已有 4750 人学习,适合需要参照软件工程规范化文档结构、理解停车场调度与计费逻辑的读者,也可用于改写为其他管理系统课程设计。
1. 单门狭长停车场:一份软件工程课程设计到底要交什么
一个停车场只有 n 个车位、只有一道大门,车按到达先后从最里侧往外排;里侧的车要出去,外边的车得先一辆辆退出来让路。这个设定并非出题人故意刁难,它正好把「停车场管理系统」从一道普通的增删改查练习,变成了一次完整的需求分析训练——你得先把物理世界里的让路规则想明白,才谈得上写代码。这份软件工程课程设计报告的交付物其实有两件:一份能跑起来的 C 程序,和一份把需求、概要设计、详细设计、测试串起来的说明书。前者证明你写过,后者证明你想过。在互联网上能找到的同类课设里,十份有八份只贴一段代码、没有设计过程,答辩时被追问一句「为什么停车场用栈」就卡壳。下面几章按交付顺序拆开讲,从需求口径一路落到验收用例。
2. 需求分析:把“停车难、乱收费”拆成五条能验收的功能点
2.1 从“单门 + 狭长”这个约束倒推系统边界
需求分析的第一个动作,是把题目里的文字描述翻译成约束条件,而不是急着写功能列表。这套题目的约束非常硬:停车场是狭长的一条,只有一道大门,车辆按到达时间早晚从最里面向大门口依次停放,最先到的车停最里侧;停车场满了,后来的车只能在大门外的便道上等,一旦有车开走,便道上排第一的车立刻进场;场内某辆车要走,它之后进场的车必须先退出去让路,等它开走再按原次序回来;每辆车按停留时长交费;便道上的车如果还没进场就要走,允许直接离开且不收费,剩余排队次序不变。
把这五条约束读三遍会发现,它们其实已经规定了数据结构的形状。「从最里侧往外排、后进的车挡在先来车的外面」是典型的后进先出,让路动作就是弹出再压回;「便道上排第一的车先进场」是先到先得,就是队列。需求分析章节写到这个份上,概要设计里的选型就不再是拍脑袋。我带学生做这类题时,会要求他们在需求文档里专门写一节「业务约束」,把物理规则逐条列出来,每条后面标注它影响哪个模块。这一步多花二十分钟,后面能省两天。
系统的服务对象也要先分清。车主关心的是「我停哪、我交多少钱」,管理员关心的是「还有几个空位、今天收了多少」,这两类人对界面的期待完全不同。课程设计规模有限,通常只做管理员视角的单机控制台程序,但报告书里要明确写出这个取舍,否则评阅时会认为你没做角色分析。
2.2 五条功能需求的验收口径
功能需求最怕写成「实现车辆信息管理」这种谁都对得上的话。合适的写法是给每条需求配一组输入、处理、输出和验收标准,让评阅人一眼能判断你做没做到。下面这张表是按这个思路整理的,可以直接搬进报告书的 2.x 节。
| 功能 | 输入 | 处理 | 输出 | 验收标准 |
|---|---|---|---|---|
| 记录车辆信息 | 车辆次序号、进场时刻 | 分配车位号,写入车辆表 | 车位号 + 提示语 | 连续录入 3 辆车,车位号依次为 0、1、2 |
| 动态分配车位 | 当前场内车辆数 | 未满则入场,已满则进便道 | 入场位号或“已满”提示 | 第 N+1 辆车必须落在便道而非场内 |
| 费用结算 | 离场车辆次序号、离场时刻 | 停留时长 × 单价 | 停留小时数 + 金额 | 进场 8 点、离场 11 点,输出 3 小时、3.60 元 |
| 车位使用状况 | 无 | 遍历场内车位状态 | 空位数、占用位号 | 停 3 辆后显示空位 N-3 |
| 退出系统 | 菜单选择 4 | 结束进程 | 无 | 返回操作系统,无残留输出 |
单价和车位数在题目里是「可预设」的,所以要设计成宏或者配置项,而不是散落在代码里的魔法数字。验收时把 N 从 100 改成 5,重新跑一遍满场逻辑,能过就说明参数化做到了位。这个改参数的动作,也是答辩时最容易被要求现场演示的一项。
2.3 非功能需求:可扩展、可靠、易用,得写成能检验的句子
课程设计报告里几乎人人都会写「本系统具有可扩展性、可靠性、易用性」,但十个里有九个后面接不出一句具体的话。可扩展性落到这个项目上,具体就是三件事:车位数、便道容量、计费单价必须由宏或者配置常量控制;新增一种车型只改结构体、不动主流程;菜单项的增加不需要改动已有分支的逻辑。可靠性具体是:任何一次非法输入(比如输入不存在的车辆次序号)都不能让程序崩溃或进入死循环,必须给出提示并退回菜单。易用性具体是:每一步输入都有明确提示,提示里写清取值范围和单位,操作完成后回到主菜单而不是直接结束进程。
这三点写成可检验的句子后,测试章节就有了对应内容:改 N=5 跑满场、输入 -1 和 999 跑异常、连续操作二十次看是否还停在菜单。评阅老师看到这种写法,基本不会再追问「你的可靠性体现在哪」。
2.4 用例与主流程:一辆车在系统里有三种去向
把需求收敛成用例,最清晰的方式是按车辆的三种去向划分。第一种是正常进场:车辆到达,系统判断场内是否已满,未满则分配最外侧的空位、记录进场时刻。第二种是满场等待:车辆到达时场内已满,进入便道队列尾部,此时不记录计费起点——这一条极其关键,很多实现把到达时刻当成计费起点,导致便道等待的两小时也被收费。第三种是离场:场内车辆离场要触发让路、计费、便道补位三个动作;便道上的车辆离场则直接从队列中摘除,不产生费用。
主流程可以这样描述:程序启动后显示菜单,包含初始化、车辆进入、车辆离开、退出四项;用户输入编号,程序校验编号合法性后分发到对应模块;模块执行完毕返回菜单,等待下一次输入;选择退出则结束进程。这段描述配上概要设计的处理流程说明,就构成了报告书第三章的主干。用例不必画得多漂亮,但一定要覆盖「满场」「便道有人」「目标车在最里侧」这三个边界,它们是后面测试用例的来源。
3. 数据结构选型:停车场用栈、便道用队列,这是物理约束决定的
3.1 停车场必须是栈:后进先出不是巧合
有人会问,停车场车位是一排,为什么不用数组顺序存而要强调栈。原因在于「让路」这个动作:里侧的车要出去,挡在外面的车必须先退出,等目标车开走后,这些车再按原来的次序回来。退出时最后退出的那辆车最先回来,这正是后进先出。用数组顺序存当然也能实现,但你得手动记录每一辆被挪走的车的位置和顺序,代码里会多出一堆临时变量;用栈的思维写,退出去就是压栈、回来就是弹栈,顺序天然正确。
实现上不必真的写一个 stack 结构体。这个项目里最省事的做法是:用一个数组yard[N]存车辆次序号,用一个整型yard_size表示当前场内车辆数,数组下标 0 对应最里侧车位,yard_size - 1对应最外侧。进场就是yard[yard_size++] = id,出场时把pos之后的所有元素依次弹出到临时栈、目标车移除、再把临时栈弹回数组。数组尾部当栈顶用,省掉一套 push/pop 函数,代码短且不容易错。
提示:报告书里写「采用栈结构模拟停车场」时,最好补一句「以数组尾部为栈顶,避免额外的指针管理」,评阅时显得你确实想过实现细节,而不是抄了个名词。
3.2 便道必须是队列:先到先得的公平性
便道的规则是「排在便道上的第一辆车先进场」,谁先来谁先进,这是队列的定义。用环形队列实现最稳:一个wait_queue[M]数组存车辆次序号,配q_head、q_tail、q_count三个变量。入队时判断q_count == M是否已满,不满就写wait_queue[q_tail]再q_tail = (q_tail + 1) % M;出队时取wait_queue[q_head]再q_head = (q_head + 1) % M,同时q_count--。用取模而不是直接加一,是为了让便道长期运行也不会因为下标越界而失效。
便道还有一条容易被忽略的规则:便道上的车如果没进场就要走,允许直接离开、不收费,且剩余车辆的次序不变。这意味着队列要支持「从中间删除一个元素」。环形队列做这件事的做法是:找到目标元素的下标,把它之后到队尾之间的元素整体前移一位,q_count--,队尾指针回退一格。代码不长,但必须写,否则测试时「便道车中途离开」这条用例直接过不去。
3.3 结构体与全局数组:先把内存布局定下来
数据结构定完,接着是把字段落到结构体上。车辆信息至少要有次序号、进场时刻、离场时刻、占用车位、当前状态五个字段,其中状态字段用来区分「在场内」「在便道」「已离场」,避免在业务逻辑里反复用多个数组交叉判断。
#include <stdio.h> #include <stdlib.h> #define N 100 /* 停车场车位数,改这一个数就能扩展规模 */ #define M 100 /* 便道等待位数 */ #define FEE 1.2 /* 每小时停车费,单位元 */ #define MAX_CAR 1000 /* 车辆次序号上限,一次运行内可处理的车辆总数 */ typedef struct { int car_id; /* 车辆次序号,进场时按到达顺序分配 */ int arrive; /* 真正进入停车场的时刻,24 小时制整点 */ int leave; /* 离场时刻 */ int slot; /* 占用的车位号,0 号为最里侧 */ int state; /* 0=已离场 1=场内 2=便道等待 */ } Car; typedef struct { int car_id; /* 该车位上的车,-1 表示空位 */ } Slot; Car cars[MAX_CAR]; Slot yard[N]; /* 停车场,数组尾部当栈顶 */ int yard_size = 0; /* 场内车辆数 */ int wait_queue[M]; /* 便道,环形队列,存车辆次序号 */ int q_head = 0, q_tail = 0, q_count = 0;这段代码有三处值得在报告书里解释。第一,car_id直接用作cars数组下标,查找车辆信息是 O(1),代价是数组要按最大车辆数开,属于典型的以空间换时间,课程设计规模下完全划算。第二,yard里存的是车序号而不是车辆结构体本身,这样挪车时只移动一个 int,数据量小、出错概率低。第三,arrive字段的注释特意写了「真正进入停车场的时刻」,这是为了提醒自己便道等待期间不计费——这个细节后面第 4 章还会展开。
如果按次序号直接索引数组,有个前提要守住:录入车辆时次序号不能重复。做法是在进入模块里先检查cars[id].state,如果不为 0 就提示「该次序号已被占用」,让用户重新输入。少了这一步,后录入的同号车会覆盖前一辆的信息,测试时表现为「某辆车收费金额莫名其妙变成 0」。
3.4 让路用的临时栈:为什么不能省
车辆离开时,把目标车外侧的车退出去需要一块临时空间。有人图省事,直接把这些车的信息打印出来让用户「手动记住」,等目标车开走后重新录入——原版实现里就是这么干的,提示语写着「请让场退出的车再依次进场」。这种写法在演示时能跑通,但它把系统该承担的状态管理推给了用户,属于设计缺陷。
正确的做法是在离场函数内部开一个局部数组int tmp[N]当临时栈,配合一个top指针。目标车外侧的车从外往里依次压入tmp,目标车移除后,再从tmp里依次弹出写回yard。临时栈是函数局部变量,生命周期只在一次离场操作内,不占全局空间,也不会污染停车场状态。这里有个小坑:tmp的大小要按N开,不能按「当前车辆数」开,否则编译期就过不去。局部数组开在栈上,N=100 时只占 400 字节,完全不用担心栈溢出,但如果把 N 调到几万,就得改成动态分配或者全局静态数组。
4. 详细设计与编码落地:四个函数的职责边界与关键代码
4.1 main 与 menu:一个能回到原点的控制循环
主函数只做一件事:把菜单拉起来。菜单函数负责三件事——打印选项、校验输入、分发到对应模块。这里有个设计选择值得说清楚:模块执行完是「返回菜单」还是「直接结束进程」。正确做法是返回,让用户能连续操作。实现上不要用递归调用menu(),而要用循环。
void menu(void) { int choice; while (1) { /* 循环而不是递归,避免连续操作后栈溢出 */ printf("\n********** 停车场管理系统 **********\n"); printf(" 1. 初始化(日终清零)\n"); printf(" 2. 车辆进入\n"); printf(" 3. 车辆离开\n"); printf(" 4. 查看车位使用状况\n"); printf(" 5. 退出系统\n"); printf("***********************************\n"); printf("请输入选项(1-5): "); if (scanf("%d", &choice) != 1) { /* 输入非数字时清空缓冲区 */ while (getchar() != '\n') ; printf("输入无效,请重新选择。\n"); continue; } switch (choice) { case 1: init_system(); break; case 2: car_enter(); break; case 3: car_leave(); break; case 4: show_status(); break; case 5: printf("系统已退出。\n"); return; default: printf("选项超出范围,请输入 1-5。\n"); } } }这段代码的重点不在打印排版,而在两处防御。scanf的返回值判断是为了挡住「用户输入字母」这种情况,不做判断的话,非数字输入会留在缓冲区里被反复读取,程序进入无限打印菜单的死循环,这是控制台程序最常见的翻车方式。default分支是为了挡住越界数字,配合前面的类型校验,菜单层就基本不会把脏数据传给业务模块。业务模块内部的输入也一样要挡住,原则是:任何一处scanf后面都要有校验,没有校验的输入就是一次潜在的崩溃。
4.2 初始化:清零不是走过场
初始化模块把场内车位、便道队列、车辆状态全部复位。看起来简单,但它是整个系统「可重复实验」的前提——测试时要反复从空场开始跑用例,没有可靠的初始化,每次都得重启进程。
void init_system(void) { int i; for (i = 0; i < N; i++) yard[i].car_id = -1; /* -1 表示空位,不用 0,因为 0 是合法车号 */ yard_size = 0; for (i = 0; i < M; i++) wait_queue[i] = -1; q_head = q_tail = q_count = 0; for (i = 0; i < MAX_CAR; i++) { cars[i].car_id = -1; cars[i].state = 0; cars[i].arrive = 0; cars[i].leave = 0; cars[i].slot = -1; } printf("初始化完成,当前空位 %d 个。\n", N); }这里用 -1 而不是 0 作为空位标记,是个容易忽略的细节。车辆次序号从 0 开始,如果空位也用 0 表示,判断「这个位置有没有车」就会永远成立,程序会把 0 号车位当成一直有车。空位标记必须是一个不可能是合法车号的数,-1 最合适。
4.3 车辆进入:先判满,再定位,最后记时间
进入模块的处理顺序是三段:判断场内是否已满、未满则分配最外侧空位、记录进场时刻。满了就进便道队列,同时把车辆状态标成 2。
void car_enter(void) { int id, now; printf("请输入车辆次序号(0-%d): ", MAX_CAR - 1); if (scanf("%d", &id) != 1 || id < 0 || id >= MAX_CAR) { while (getchar() != '\n') ; printf("次序号非法。\n"); return; } if (cars[id].state != 0) { /* 同号车不能重复录入 */ printf("该次序号已被占用,请核对。\n"); return; } printf("请输入进场时刻(0-23 整点): "); if (scanf("%d", &now) != 1 || now < 0 || now > 23) { while (getchar() != '\n') ; printf("时刻非法。\n"); return; } if (yard_size < N) { /* 场内还有空位 */ yard[yard_size].car_id = id; cars[id].slot = yard_size; /* 车位号 = 当前车辆数,即最外侧 */ cars[id].arrive = now; cars[id].state = 1; yard_size++; printf("入场成功,车位号 %d,计费起点 %d 时。\n", cars[id].slot, now); } else if (q_count < M) { /* 场内满,进便道 */ wait_queue[q_tail] = id; q_tail = (q_tail + 1) % M; q_count++; cars[id].state = 2; cars[id].arrive = -1; /* 还没真正进场,计费起点留空 */ printf("场内已满,车辆进入便道等待,前面还有 %d 辆。\n", q_count - 1); } else { printf("停车场与便道均已满,请稍后再来。\n"); } }注意cars[id].arrive = -1这一行。便道车辆虽然记录了到达,但计费起点必须等它真正进场时才写。如果这里图省事填了now,后面计费就会把等待时间算进去,测试用例里「便道等 2 小时后进场、再停 1 小时」应该收 1 小时的钱,程序却会收 3 小时,一对账就露馅。
4.4 车辆离开:让路、计费、便道补位三件事
离场是整套系统里逻辑最重的一段,要做三件事:把目标车外侧的车退到临时栈、结算费用、把便道队首补进空出来的位置。
int car_leave(void) { int id, now, tmp[N], top = 0, pos = -1, i; double fee; printf("请输入离场车辆次序号: "); if (scanf("%d", &id) != 1 || id < 0 || id >= MAX_CAR) { while (getchar() != '\n') ; printf("次序号非法。\n"); return -1; } /* 情况一:车在便道上,直接出队,不收费 */ if (cars[id].state == 2) { int idx = q_head, cnt = 0; while (cnt < q_count && wait_queue[idx] != id) { idx = (idx + 1) % M; cnt++; } if (cnt == q_count) { printf("便道中未找到该车。\n"); return -1; } /* 后面的车整体前移一位,保持排队次序 */ while (idx != q_tail) { int next = (idx + 1) % M; wait_queue[idx] = wait_queue[next]; idx = next; } q_tail = (q_tail - 1 + M) % M; q_count--; cars[id].state = 0; printf("便道车辆直接离场,未进场不计费。\n"); return 0; } if (cars[id].state != 1) { printf("该车不在场内。\n"); return -1; } /* 情况二:车在场内,先定位 */ for (i = 0; i < yard_size; i++) if (yard[i].car_id == id) { pos = i; break; } /* 把 pos 外侧的车依次退到临时栈 */ for (i = yard_size - 1; i > pos; i--) tmp[top++] = yard[i].car_id; printf("请输入离场时刻(0-23 整点): "); if (scanf("%d", &now) != 1 || now < 0 || now > 23) { while (getchar() != '\n') ; printf("时刻非法。\n"); return -1; } fee = calc_fee(id, now); cars[id].leave = now; cars[id].state = 0; yard_size = pos; /* 场内容量收缩到目标车位置 */ /* 临时栈里的车按原次序回场,车位号重新连续 */ while (top > 0) { int back = tmp[--top]; yard[yard_size].car_id = back; cars[back].slot = yard_size; yard_size++; } printf("车辆 %d 停留 %d 小时,应收 %.2f 元。\n", id, hours_between(cars[id].arrive, now), fee); /* 便道队首补位 */ if (q_count > 0) pull_from_queue(now); return 0; }这段代码里有几个决定成败的细节。tmp的压栈顺序是i从yard_size - 1递减到pos + 1,也就是从最外侧往里压,弹出时自然是从栈顶(最靠近目标车的那辆)开始回场,顺序正好和原来一致。yard_size = pos这一步把目标车「裁掉」,然后回场的车从pos开始重新占位,车位号自动连续,不需要额外整理。便道补位放在计费之后,是为了让补位的车拿到新的进场时刻,而不是沿用离场车的时刻。
4.5 计费与便道补位的边界处理
计费函数看上去只有一行乘法,实际边界不少:停留时长可能为 0(同一小时进出)、可能跨零点(22 点进、次日 2 点出)、可能因为用户输入错误变成负数。
int hours_between(int in, int out) { int h = out - in; if (h < 0) h += 24; /* 跨零点,按 24 小时制回绕 */ if (h == 0) h = 1; /* 不足 1 小时按 1 小时计,符合停车场惯例 */ return h; } double calc_fee(int id, int out) { return hours_between(cars[id].arrive, out) * FEE; } void pull_from_queue(int now) { int id; if (q_count == 0) return; id = wait_queue[q_head]; q_head = (q_head + 1) % M; q_count--; yard[yard_size].car_id = id; cars[id].slot = yard_size; cars[id].arrive = now; /* 计费起点重置为真正进场时刻 */ cars[id].state = 1; yard_size++; printf("便道车辆 %d 补位进场,车位号 %d,计费起点 %d 时。\n", id, cars[id].slot, now); }pull_from_queue里重置arrive是整个计费正确性的最后一道保险。便道车辆在等待期间arrive是 -1,如果补位时不重新赋值,计费时会拿 -1 去减,算出一个荒谬的金额。测试时专门造一条「便道等待后补位」的用例,就是为了钉住这个点。
5. 避坑与排查:这套课设最容易翻车的五个地方
5.1 计数循环里的空分号:车场明明有空位却提示已满
现象:连续录入三辆车后,第四辆车明明还有空位,程序却提示「停车场已停满」。原因:判断语句后面多写了一个分号,if (yard[i].car_id == -1);这样写,if 绑定的是一条空语句,后面的计数逻辑变成了无条件执行,场内车辆数直接被算成 N。这类错误编译不报、语法不违法,靠肉眼看代码很难发现。解决:把判断和语句块写在同一行或者用花括号包住,if (cond) { count++; };更彻底的做法是干脆不用计数循环,直接维护yard_size变量,进场加一、出场减一,从源头上消灭这类遍历计数。
5.2 便道只打印不入队:满场之后车辆凭空消失
现象:停车场满员后继续录入车辆,程序打印「请停在便道上」,但下一辆车离场时,便道上并没有车补进来,之前那辆车像是消失了。原因:进入模块在满场分支里只写了printf,没有真正执行入队操作,wait_queue和q_count从未更新。这种「界面做了、数据没做」的半成品在原版实现里很常见,因为打印语句能立刻看到效果,而入队操作看不到。解决:凡是提示语里出现「已进入便道」「已排队」这类字眼,后面必须跟一句真正的状态写入,写完顺手在下一行加个断言或者调试打印q_count,确认数量变了。
5.3 时间差为负与跨零点:收费算出负数
现象:录入进场时刻 22、离场时刻 2,程序输出停留 -20 小时、收费 -24 元。原因:直接用out - in计算时长,没有考虑 24 小时制的回绕。解决:统一走hours_between函数,负数加 24,零按 1 小时处理。凡是涉及时间的计算都不要在业务代码里直接写减法,抽成函数之后,跨零点、同小时进出这些边界只在一个地方处理,改起来也只改一处。
5.4 菜单函数递归调用:连续操作几十次后程序自己退出
现象:功能都正常,但连续操作二三十次后程序毫无征兆地结束,没有任何报错。原因:每个业务模块结束时都调用了menu(),而menu()又会在退出分支里return,形成一层层嵌套调用,每操作一次栈上就多一帧,操作次数多了栈空间耗尽。控制台程序栈空间有限,这种写法在小规模演示时看不出问题,长时间操作必崩。解决:业务模块只负责处理数据然后return,回到menu的while循环里继续下一轮,全程只有一层调用。这个改动的收益不只是防崩溃,还让「模块」和「界面」的职责彻底分开,后面想把控制台换成图形界面时,业务函数一行都不用改。
5.5 数组边界:声明 N-1 个却按 N 个用
现象:把车位数改成 5 做满场测试,录入第 5 辆车时程序输出正常,但第 6 辆车的处理变得莫名其妙,或者直接崩溃。原因:数组声明成Slot yard[N-1],循环却写i <= N - 1,实际访问到了第 N 个元素,越界写坏了相邻变量的内存。这种错误在 N=100 时不明显,一改小参数就暴露,属于典型的「测试参数帮你抓 bug」。解决:数组一律声明为yard[N],循环统一写i < N,不写<=。报告书里把这条写进编码规范,答辩时被问到「你怎么保证不越界」,这就是现成的答案。
6. 测试与答辩:用一组用例把「我确实做完了」讲清楚
测试章节最忌讳只贴几张运行截图。截图证明程序能跑,证明不了逻辑对。合适的做法是先给一张用例表,每条用例写清前置条件、输入、预期输出,再配截图。
| 编号 | 前置条件 | 操作 | 预期结果 |
|---|---|---|---|
| T1 | 空场,N=5 | 连续录入 3 辆车 | 车位号依次 0、1、2,空位 2 |
| T2 | 场内 5 辆 | 录入第 6 辆 | 提示已满,进入便道,q_count=1 |
| T3 | 场内 5 辆、便道 1 辆 | 0 号车(最里侧)离场 | 1-4 号车让路后回场,便道车补位到 4 号位 |
| T4 | 3 号车 8 时进场 | 11 时离场 | 停留 3 小时,收费 3.60 元 |
| T5 | 便道有 2 辆车,队首为 7 号 | 7 号车直接离场 | 不收费,剩余 1 辆次序不变 |
| T6 | 便道车等待 2 小时后补位,再停 1 小时 | 离场 | 计费 1 小时,1.20 元 |
T3 和 T6 是两张王牌。T3 验证让路逻辑和补位逻辑,答辩老师最爱问「最里侧的车怎么出来」,把这条跑一遍比讲十分钟都管用。T6 验证计费起点,能答上来说明你不是照着抄的。T1 到 T6 全过,再补一组异常输入:次序号输入 -1、999、字母,时刻输入 25,看程序是不是都能提示后回到菜单。异常用例不用多,三条足够,但它们能挡住「程序健壮性如何」这个必问题。
答辩现场有个实用技巧:把 N 改成 5 再演示。N=100 时满场要录一百次,没人有耐心看;N=5 时三分钟能把进场、满场、让路、计费、补位全演一遍,节奏完全在自己手里。这也顺带证明了参数化确实有效。
我自己的习惯是,每次交课前把用例表打印出来,一条一条手动敲,敲完在表上打勾,而不是跑一遍程序觉得「差不多都对」就交。有一回 T3 就是在这张表上挂掉的——让路之后车位号没重排,回场的车全挤在原来的高位上,界面上看不出异常,但再进一辆车就覆盖了数据。从那以后我每次改完离场函数,都强制走一遍「最里侧车离场 + 便道补位」这条链路,不管改动多小。希望这套结构和用例表能帮到你,照着填一遍,报告书和程序基本就齐了。
本文还有配套的精品资源,点击获取