🪟 第18章 Visual C++与SQL Server实现酒店管理系统
📄 一句话概括

四层结构:界面、业务、数据、数据库。分层的价值是「改一处不牵动全局」。 三个项目换三种语言,表和分层几乎不变——真正稳定的是结构,语言只是外壳。 冒烟测试就是几条「一致性断言」:改完代码先跑断言,再点界面。断言过不了,点什么都是白点。

📝 本章和前面几章的关系

第 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 秒实验

  1. 把冒烟检查里所有 COUNT(*) 换成「COUNT(*) 与期望值比较」,不符合就抛异常。 这就是一个最小的自动化测试。
  2. 给「房态已住但无住单」那条加一个反向查询(有在住记录但房态是空闲), 两条都通过才算数据一致。
  3. 想一想:如果客户要求「同一个客人不能同时住两间房」,这条规则该放哪? 表上(部分唯一索引,WHERE state='在住')+ 业务层提示—— 库层兜底、业务层给友好提示,两层配合。

常见误区

  1. 「分层是大项目才需要的」。 恰恰相反:小项目更容易「全塞一个文件」,然后改不动。 分层不用四层,最少「界面 + 数据」两层也比不分好。
  2. 「冒烟测试就是点一遍界面」。 点界面靠眼睛,会漏。 断言靠数字,不会漏。
  3. 「学过这个项目就等于会做系统了」。 会做「这一种」系统而已。 真正迁移的是「表设计、事务边界、参数化、分层」这四样。
自测三问(点开对答案) 1. 四层结构各放什么? —— **界面层放窗体控件、业务层放流程和计算、数据层放实体类和公共类、数据库放表和约束。** **判断标准:换个数据库要改几个文件。** 2. 三个项目为什么换语言、库结构却差不多? —— **因为稳定的是「表设计和分层」,语言只是外壳。** **VC++、C#、Java 换的是界面和连接方式,业务骨架没变。** 3. 什么是「一致性断言」? —— **一条「必须等于 0」的检查**(比如「房态已住但无住单」)。 **它比人工看表可靠,而且能挂到每次改动之后自动跑。**

下一篇进第 19 章 19.1,讲企业人事管理系统: 同样是桌面程序,但业务主线变成了「组织、人员、薪酬」三条——看看哪些东西能直接搬过来。

相关笔记