逆向软件设计和开发——图书管理系统
(1)项目来源
该项目是从一位高年级的学长那里分享的一份关于他的课程作业。项目实现了基本的图书管理、客户管理和订单创建功能,所有数据通过文本文件存储。代码结构清晰,包含 Book、Customer、Order、BookStore 等类,适合作为学习 C++ 面向对象编程的案例。我从同学处获得源代码文件 网上购书3.cpp,并基于此进行缺陷分析和二次开发。
(2)运行环境与运行结果截图
运行环境:
操作系统:Ubuntu 22.04 LTS (也可在 Windows 下使用 MinGW)
编译器:g++ 11.4.0
编译命令:g++ -std=c++11 网上购书3.cpp -o bookstore
依赖文件:books.txt、customers.txt
运行结果截图

选择 1 显示书本列表:

选择 3 创建订单,输入客户 ID 和书本信息后成功生成订单,并显示订单明细及总金额。

(3)主要问题列表与改善思路
问题 1:库存修改未持久化
现象:创建订单后,内存中的库存减少,但 books.txt 文件未更新。下次启动程序时库存仍为原值,导致数据不一致。
改善思路:在程序退出前(或每次库存变化后)将 books 数据写回 books.txt 文件。同时提供保存命令或自动保存。
问题 2:历史订单未加载
现象:程序启动时仅加载书籍和客户,不加载之前保存的订单文件 orders.txt,因此 显示所有订单 只能看到本次运行创建的订单。
改善思路:在 BookStore::loadData 中增加加载订单的方法,解析 orders.txt 并重建订单对象。
问题 3:文件格式依赖脆弱,错误处理不足
现象:若 books.txt 或 customers.txt 格式不符(如书名中包含逗号),读取可能失败;文件不存在时仅打印错误,后续操作可能崩溃。
改善思路:增加文件存在性检查,若文件无法打开则提示用户并创建默认文件;使用更稳健的解析方式(如逐行读取并用逗号分割)。
问题 4:输入未处理非数字字符
现象:主菜单选择或输入数量时,若用户输入字母,cin 进入错误状态,导致程序无限循环。
改善思路:使用 cin.fail() 检测,清空缓冲区并重新提示输入。
问题 5:代码风格陈旧(使用 NULL)
现象:多处使用 NULL 而非 C++11 的 nullptr。
改善思路:将所有 NULL 替换为 nullptr,提高类型安全。
问题 6:订单部分成功逻辑可能引发歧义
现象:创建订单时,若某些书库存不足,程序会跳过这些书而只添加有库存的书,最终生成部分订单。用户可能期望要么全部成功要么失败。
改善思路:增加选项,允许用户选择“部分下单”或“全部失败”;也可保持当前行为,但在提示中明确说明。
(4)改进后的代码(关键部分)
改进一:库存持久化

改进二:加载历史订单

改进三:输入错误处理

(5)重构后的软件测试截图
测试用例 1:库存持久化

测试用例 2:历史订单加载

测试用例 3:输入异常处理

测试用例 4:文件不存在处理


(6)总结
难点与耗时
最难的部分:解析历史订单文件。原订单文件是以人类可读的文本格式存储,格式不固定,难以逆向解析。最终我改为保存两份文件:一份供人阅读的 orders.txt,一份结构化的二进制 orders.dat 供程序加载。这花费了大约 3 小时。
耗时较久:理解原有代码的数据流和依赖关系。由于原代码注释较少,需要逐行跟踪变量变化,特别是 BookStore::createOrder 中的部分成功逻辑,花了 2 小时理清。
输入验证处理:虽然简单,但需考虑所有输入场景(菜单、ID、数量、ISBN),确保程序健壮,也花费了一些时间。
对逆向软件工程的思考
通过这次二次开发,我深刻体会到“逆向软件工程”不仅是修改代码,更是理解原作者的意图和设计局限。优秀的二次开发应在保持原有架构的基础上,增强功能、修复缺陷,而不是推翻重来。例如,我尽量复用原有的类和方法,只在必要处增加新功能,保证了代码的可维护性。

浙公网安备 33010602011771号