最小可用版本
add / list / find / save
先完成最小闭环,再增加修改、删除、排序和统计,始终保持程序可运行。
Easy explanation
一个好项目先解决清楚的问题,再选择恰好够用的工具。图书管理、成绩管理、任务管理和游戏角色管理都可以成为选题,但必须明确用户是谁、保存什么记录、最常执行哪些操作、出现错误时怎样恢复。没有需求边界,代码会不断增加却始终无法完成。
结课项目要经历分析、设计、实现、测试和演示。每一阶段都留下可检查成果:需求清单、数据模型、菜单草图、测试表和说明文档。功能不是越多越好,稳定完成核心增删改查、搜索排序、统计和保存,比十个半成品功能更有价值。
Core concepts
add / list / find / save
先完成最小闭环,再增加修改、删除、排序和统计,始终保持程序可运行。
UI -> Service -> Repository
菜单负责交互,业务层负责规则,存储层负责文件,避免所有代码塞进 main。
int id;
每条记录使用唯一编号,显示名称可以重复或修改,关联和删除仍然可靠。
requirements + tests + demo
功能只有通过约定测试、保存成功并能演示,才算完成。
optional<Item> findById(int id);接口明确表达记录可能不存在,不用特殊假对象。
sort(items.begin(), items.end(), comparator);排序规则独立,菜单只负责选择规则。
bool save(const vector<Item>& items);存储函数返回成功状态,界面根据结果提示用户。
assert(service.remove(999) == false);把找不到记录等失败情况写进固定测试。
Complete examples
Example 1
Book 只保存一本书的数据,Library 负责唯一编号、借阅规则、查找和容器,main 负责菜单。文件读写还可以放入单独 Repository。
先写类接口和空函数,让程序能编译,再一次完成一个用例。这样任何时候都有可运行版本。
struct Book
{
int id;
string title;
string author;
bool borrowed = false;
};
class Library
{
private:
vector<Book> books;
int nextId = 1;
public:
int add(string title, string author);
bool remove(int id);
Book* find(int id);
vector<Book> search(string keyword) const;
void sortByTitle();
bool load(const string& path);
bool save(const string& path) const;
};
int main()
{
Library library;
library.load("books.txt");
runMenu(library);
library.save("books.txt");
}
Example 2
“删除图书”不是一句代码,而是一组行为:存在时删除并返回 true,不存在返回 false,删除后无法查到,保存重启后仍然不存在。
把这些行为写成测试,再实现函数直到全部通过。每新增功能都先写成功和失败用例,项目会比只靠手工点击稳定得多。
void testRemoveBook()
{
Library library;
int id = library.add("C++ 入门", "TeacherMa");
assert(library.find(id) != nullptr);
assert(library.remove(id) == true);
assert(library.find(id) == nullptr);
assert(library.remove(id) == false);
}
void testEmptySearch()
{
Library library;
assert(library.search("missing").empty());
}
Algorithm thinking
不要写“完成图书系统”这种巨大任务。改成:用户可以添加一本合法图书;用户可以按编号找到图书;不存在编号会提示;数据重启后仍保留。每条故事都能在短时间内实现和测试,完成后再进入下一条。
算法和数据结构要服务需求。主记录数量不大且经常排序展示,可以用 vector;需要按编号频繁查询可增加 unordered_map 索引;撤销操作可用 stack;待处理预约可用 queue。必须解释选择理由,而不是为了展示知识强行使用所有容器。
选择一个主题,写出必须功能、可选功能和明确不做的功能。
画类图、选择唯一编号、列出类方法和文件格式。
先做最小闭环,每增加一个功能立即测试并保持可运行。
使用固定示例数据走完整流程,展示正常、边界和错误恢复。
Common mistakes
核心流程还没运行就增加颜色、动画和复杂权限,项目容易永远完不成。
菜单、规则、排序和文件混在一起,无法独立测试和修改。
不存在编号、空列表、坏文件和重复数据必须成为正式演示的一部分。
持久化会影响编号和模型,应尽早建立最小保存加载闭环。
先准备一个最小输入,只保留能够重现问题的几行数据;再在关键步骤输出变量值,确认程序究竟在哪一步偏离预期。编译错误从第一条开始处理,运行错误则比较“实际结果”和“期望结果”。修复后别只重跑原来的例子,还要增加空输入、边界值和错误输入,防止问题换一个形式再次出现。
Homework
建议按顺序完成。前四题帮助巩固语法和基本操作,第五到第七题要求把多个知识点组合起来,第八题是小项目。每道题都要先写输入、处理、输出三行计划,再开始敲代码;程序运行后至少测试正常情况、边界情况和错误情况。
选择题目,写用户、问题、八项核心功能、三项可选功能和不做范围。
画类图,标出字段、方法、唯一编号、继承或组合,并解释每个容器选择。
先用假数据完成菜单循环、输入验证、返回和退出,确认交互清楚。
完成新增、查看、修改、删除,覆盖重复编号、找不到目标和空字段。
加入至少两种排序、一种搜索和三项统计,说明时间复杂度。
实现启动加载和退出保存,处理首次运行、空文件和坏记录。
列出至少二十个测试,记录输入、期望、实际和修复过的问题。
提交源代码、README、示例数据、类图、测试报告,并完成五分钟现场演示。
本课验收标准:项目可从干净目录编译运行;具备可靠输入、完整 CRUD、排序搜索统计、文件保存加载和错误恢复;至少二十个测试有记录;学生能独立解释类、容器和算法选择。
提交内容应包括源代码、三组测试输入与输出、一个曾经出现的错误及修复方法。最后请用自己的话解释本课最重要的概念;如果只能照着代码念,还需要再独立重写一次核心例子。