四层结构:界面、业务、数据、数据库。分层的价值是「改一处不牵动全局」。 三个项目换三种语言,表和分层几乎不变——真正稳定的是结构,语言只是外壳。 冒烟测试就是几条「一致性断言」:改完代码先跑断言,再点界面。断言过不了,点什么都是白点。
第 18 章走完了一个完整项目:需求 → 设计 → 建库 → 公共类 → 登录 → 主窗体 → 业务模块。 这一节把「留下的东西」收拾一下。 教材 18.8 分两小节:这个项目真正训练的是什么、下一个人事系统换了什么。 后者是「预告」,也是全书的伏笔——它告诉你「接下来两章不是重复,是变奏」。 冒烟测试那一段是真跑的:六条检查项、一条一致性断言,这套东西可以直接搬到任何项目里用。
一、分层:四层,各管一段
| 层 | 这一层的东西 | 不该出现在这里 |
|---|---|---|
| 界面层 | 窗体、控件、点击事件 | SQL 语句 |
| 业务层 | 开房、结账的计算与流程 | 控件名、SELECT |
| 数据层 | Room 实体类、Db 公共类 |
业务判断 |
| 数据库 | 表、约束、索引 | 业务规则(能放约束的除外) |
分层的价值不是「好看」,是「改一处不牵动全局」。 比如把 SQL Server 换成 MySQL——只改数据层,界面和业务一行不动。 怎么判断层分得对不对? 有个简单测试:「换个数据库,要改几个文件?」 如果只改数据层那一个,就分对了;如果连界面都要改,说明 SQL 漏到上面去了。
二、三个项目换了什么、没换什么
| 项目 | 语言 | 变的部分 | 没变的部分 |
|---|---|---|---|
| 酒店管理 | VC++ / MFC | 界面、连接方式(ADO) | 五张表、四层结构、事务边界 |
| 人事管理 | C# / WinForm | 控件、数据绑定 | 同样的四层、同样的登录三步 |
| 学生成绩 | Java / Swing + JDBC | 事件模型、ResultSet |
同样的表关系、同样的分页汇总 |
注意「没变」那一列——这才是这本书想让你带走的东西。 语言会换、框架会换,但「表怎么设计、事务划在哪、参数化怎么写」十年都不变。 反过来说,如果你现在只会「拖控件 + 拼 SQL」, 那你学到的正好是「会变」的那部分——换个框架就得重学。 所以这一章真正的收获不是「会用 VC++ 做酒店系统」, 而是「知道一个业务系统由哪几块组成、每块该放什么」。
三、冒烟测试:整个项目跑一遍
| 检查项 | 期望 |
|---|---|
| 客房数 | 5 |
| 客人 数 | 3 |
| 在住单 | 取决于操作,但必须和房态对得上 |
| 账单数 | 等于「已退单」的数量 |
| 房态为已住但无住单 | 必须是 0 |
最后一行必须是 0。 这叫「一致性断言」—— 每次改完代码跑一遍,比人工点界面快得多,而且不会漏。 养成这个习惯: 每个项目都写几条这样的断言, 改动之后先跑断言、再点界面。 断言过不了,界面点什么都是白点。 这几条断言的写法也有讲究: 尽量写成「返回一个数,和期望值比较」, 而不是「肉眼看表格对不对」——因为肉眼会看漏,而数字不会。
四、下一步该练什么
| 方向 | 具体做什么 |
|---|---|
| 把约束补齐 | 给「同一间房只能一条在住记录」加部分唯一索引 |
| 把并发想清 | 两个前台同时开同一间房,怎么保证只成功一个 |
| 把报表做出来 | 月度营收、入住率、平均房价(都是 GROUP BY) |
| 把登录做扎实 | 密码改 PBKDF2、失败次数限制、会话超时 |
第四条最容易被跳过,但它是「能不能上线」的分界线。
一个明文存密码的系统,其他做得再好也不该上线。
第三条(报表)是「学 SQL 最见效」的地方:
GROUP BY + HAVING + 窗口函数(9.3 讲过)全在这里用得上,
而且业务价值直接可见——老板要的就是那几张表。
五、动手 Demo:四层结构、三个项目的对照、六条冒烟检查
这一段把「四层结构」和「三个项目换什么」列清楚, 最后真跑六条冒烟检查——其中最后一条是一致性断言,必须是 0。 这套检查可以直接抄进任何项目,改的只是 SQL。
checks = [
(u"客房数", u"SELECT COUNT(*) FROM tb_room"),
(u"在住单", u"SELECT COUNT(*) FROM tb_checkin WHERE state = '在住'"),
(u"账单数", u"SELECT COUNT(*) FROM tb_bill"),
(u"房态已住但无住单",
u"SELECT COUNT(*) FROM tb_room r WHERE r.state = '已住'"
u" AND NOT EXISTS (SELECT 1 FROM tb_checkin ci"
u" WHERE ci.room_id = r.id AND ci.state = '在住')"),
]
for name, sql in checks:
print(name, c.execute(sql).fetchone()[0])
实跑输出:
--------------------------------------------------------------------
本章总结:这个项目真正训练的是什么
--------------------------------------------------------------------
① 分层:四层,各管一段
--------------------------------------------------------------------
| 层 | 这一层的东西 | 不该出现在这里 |
|---|---|---|
| 界面层 | 窗体、控件、点击事件 | SQL 语句 |
| 业务层 | 开房、结账的计算与流程 | 控件名、SELECT |
| 数据层 | Room 实体类、Db 公共类 | 业务判断 |
| 数据库 | 表、约束、索引 | 业务规则(能放约束的除外) |
分层的价值不是「好看」,是「改一处不牵动全局」。
比如把 SQL Server 换成 MySQL——只改数据层,界面和业务一行不动。
这也解释了为什么教材三个项目换三种语言(VC++、C#、Java),库结构却几乎一样:
真正稳定的是「表和分层」,语言只是外壳。
② 三个项目换了什么、没换什么
--------------------------------------------------------------------
| 项目 | 语言 | 变的部分 | 没变的部分 |
|---|---|---|---|
| 酒店管理 | VC++ / MFC | 界面、连接方式(ADO) | 五张表、四层结构、事务边界 |
| 人事管理 | C# / WinForm | 控件、数据绑定 | 同样的四层、同样的登录三步 |
| 学生成绩 | Java / Swing + JDBC | 事件模型、ResultSet | 同样的表关系、同样的分页与汇总 |
注意「没变」那一列——这才是这本书想让你带走的东西。
语言会换、框架会换,但「表怎么设计、事务划在哪、参数化怎么写」十年都不变。
③ 冒烟测试:整个项目跑一遍
--------------------------------------------------------------------
| 检查项 | 结果 |
|---|---:|
| 客房数 | 5 |
| 客人 数 | 3 |
| 在住单 | 0 |
| 已退单 | 0 |
| 账单数 | 0 |
| 房态为已住但无住单 | 0 |
注意最后一行必须是 0。 这叫「一致性断言」——每次改完代码跑一遍,比人工点界面快得多,而且不会漏。
养成这个习惯: 每个项目都写几条这样的断言,改动之后先跑断言、再点界面。
断言过不了,界面点什么都是白点。
④ 下一步该练什么
--------------------------------------------------------------------
| 方向 | 具体做什么 |
|---|---|
| 把约束补齐 | 给「同一间房只能一条在住记录」加唯一索引 |
| 把并发想清 | 两个前台同时开同一间房,怎么保证只成功一个 |
| 把报表做出来 | 月度营收、入住率、平均房价(都是 GROUP BY) |
| 把登录做扎实 | 密码改 PBKDF2、失败次数限制、会话超时 |
第四条最容易被跳过,但它是「能不能上线」的分界线。一个明文存密码的系统,其他做得再好也不该上线。
这几条检查的价值在于「快」。 六条跑完不到 10 毫秒,而人工点一遍界面要五分钟、还容易漏。 「房态已住但无住单」那条是「断言」而不是「统计」。 统计是「看看有多少」,断言是「必须等于 0」——前者看一眼就过,后者会逼你处理。 这套东西可以更进一步: 把「期望值」也写进脚本, 不符合就报错退出——那就成了「自动化验收」,可以挂到每次提交之后跑。 最后一句:这一章写完的不是「一个酒店系统」, 而是「一套做业务系统的套路」——下一章换了语言、换了业务,套路一样。
30 秒实验
- 把冒烟检查里所有
COUNT(*)换成「COUNT(*)与期望值比较」,不符合就抛异常。 这就是一个最小的自动化测试。 - 给「房态已住但无住单」那条加一个反向查询(有在住记录但房态是空闲), 两条都通过才算数据一致。
- 想一想:如果客户要求「同一个客人不能同时住两间房」,这条规则该放哪?
表上(部分唯一索引,
WHERE state='在住')+ 业务层提示—— 库层兜底、业务层给友好提示,两层配合。
常见误区
- 「分层是大项目才需要的」。 恰恰相反:小项目更容易「全塞一个文件」,然后改不动。 分层不用四层,最少「界面 + 数据」两层也比不分好。
- 「冒烟测试就是点一遍界面」。 点界面靠眼睛,会漏。 断言靠数字,不会漏。
- 「学过这个项目就等于会做系统了」。 会做「这一种」系统而已。 真正迁移的是「表设计、事务边界、参数化、分层」这四样。
自测三问(点开对答案)
1. 四层结构各放什么? —— **界面层放窗体控件、业务层放流程和计算、数据层放实体类和公共类、数据库放表和约束。** **判断标准:换个数据库要改几个文件。** 2. 三个项目为什么换语言、库结构却差不多? —— **因为稳定的是「表设计和分层」,语言只是外壳。** **VC++、C#、Java 换的是界面和连接方式,业务骨架没变。** 3. 什么是「一致性断言」? —— **一条「必须等于 0」的检查**(比如「房态已住但无住单」)。 **它比人工看表可靠,而且能挂到每次改动之后自动跑。**下一篇进第 19 章 19.1,讲企业人事管理系统: 同样是桌面程序,但业务主线变成了「组织、人员、薪酬」三条——看看哪些东西能直接搬过来。
- ☐ (本篇完,下一篇 19.1 需求分析)