酒店管理系统(Hotel Management System)低层设计实战:基于 awesome-low-level-design 的 C++ 全流程实现解析
酒店管理系统(Hotel Management System)低层设计实战:基于 awesome-low-level-design 的 C++ 全流程实现解析
导读
本文以 solutions/cpp/hotelmanagementsystem 目录下的完整 C++ 实现为主线,深入拆解酒店管理系统的低层设计(LLD)全流程:从需求分析、领域建模(Guest / Room / Booking / HotelManager)到预订、入住、退房、取消预订的完整状态机流转。读完本文,你将掌握如何用 C++ 面向对象设计实现一个可扩展的酒店预订核心系统,并学会把这份实现直接复用到 LLD 面试的代码与架构讲解中。
一、系统需求分析(Requirements)
原设计文档给出了 8 条核心需求,它们是整个类设计与状态机设计的依据:
- 预订、入住、退房:系统应允许客人预订房间、办理入住(check-in)与退房(check-out)。
- 多房型管理:支持单人间、双人间、豪华房、套房等不同类型房间(当前 C++ 实现收敛为 STANDARD / DELUXE / SUITE 三档)。
- 可用性与预订状态管理:系统必须跟踪房间可用性和预订状态。
- 前台运营管理:允许酒店员工管理客人信息、房间分配与账单结算。
- 多支付方式:支持现金、信用卡、在线支付等多种支付方式(在类设计中体现为
Payment接口及其具体实现类)。 - 并发预订与数据一致性:需要处理并发预订并保证数据一致性。
- 报表与分析:为酒店管理提供报表与统计能力(本实现通过
displayAvailableRooms、displayBookingHistory、displayAllGuests、displayAllBookings等查询接口落地)。 - 可扩展性:系统应能支撑大量房间与客人的扩展。
需求 1~4 是本实现完全覆盖的核心业务闭环;需求 5~8 属于可演进方向,其中 6~8 在设计上通过集中式的 HotelManager 门面式入口为后续加锁、持久化、报表模块预留了扩展点。
二、核心类、接口与枚举设计
设计文档给出了 9 个核心构件,全部在仓库源码中落地:
| 构件 | 源码文件 | 职责 |
|---|---|---|
Guest |
Guest.hpp | 客人实体,含 ID、姓名、邮箱、电话、地址 |
Room |
Room.hpp | 房间实体,含房号、类型、价格、容量、状态 |
RoomType |
Room.hpp | 房型枚举:STANDARD / DELUXE / SUITE |
RoomStatus |
Room.hpp | 房间状态:AVAILABLE / OCCUPIED / UNDER_MAINTENANCE |
Booking |
Booking.hpp | 预订记录,含客人、房间、入住/退房日期、金额、状态 |
BookingStatus |
Booking.hpp | 预订状态:CONFIRMED / CHECKED_IN / CHECKED_OUT / CANCELLED |
Payment(接口,文档层设计) |
设计文档定义 | 定义处理支付的契约,由 CashPayment、CreditCardPayment 等实现 |
HotelManager(即文档中的 HotelManagementSystem) |
HotelManager.hpp | 中央控制器,统一管理房间、客人、预订,处理预订/入住/退房/取消 |
HotelDemo(对应 HotelManagementSystemDemo) |
HotelDemo.cpp | 演示完整业务流程 |
关于 Singleton 的说明:设计文档描述
HotelManagementSystem采用单例模式,而本仓库的 C++ 实现以HotelManager类承担该角色(构造后由main持有一个实例),未使用static单例。这种"文档给出目标设计、代码给出可编译落地版本"的组合,恰好展示了 LLD 面试中"先画类图、再落代码"的常见路径。
2.1 Guest:客人信息载体
Guest.hpp 定义的 Guest 封装了 5 个字段:guestId、name、email、phone、address,并提供全 getter 与 displayInfo()。实现见 Guest.cpp,构造时一次性传入全部信息:
Guest(std::string guestId, std::string name, std::string email,
std::string phone, std::string address);
在演示代码中,一个典型客人实例为 new Guest("G001", "John Doe", "john@example.com", "+1-555-0123", "123 Main St")。
2.2 Room:房间实体与状态
Room.hpp 中 Room 聚合了房型、状态、价格、容量四个关键属性:
enum class RoomType { STANDARD, DELUXE, SUITE };
enum class RoomStatus { AVAILABLE, OCCUPIED, UNDER_MAINTENANCE };
class Room {
private:
std::string roomNumber;
RoomType type;
RoomStatus status;
double pricePerNight;
int capacity;
public:
Room(std::string roomNumber, RoomType type, double pricePerNight, int capacity);
// getters...
void setStatus(RoomStatus status);
void displayInfo() const;
};
关键细节(见 Room.cpp):
- 新房间构造时默认
status = RoomStatus::AVAILABLE,保证初始可用性一致; setStatus是房间状态变更的唯一入口,由HotelManager在入住/退房/取消时驱动;displayInfo()会输出房号、类型、状态、每晚价格(两位小数)、容量。
演示数据:Room("101", STANDARD, 100.0, 2)、Room("201", DELUXE, 150.0, 3)、Room("301", SUITE, 250.0, 4)。
2.3 Booking:预订聚合根
Booking.hpp 中 Booking 通过指针关联 Guest 与 Room,构成预订的完整上下文:
class Booking {
private:
std::string bookingId;
Guest* guest;
Room* room;
std::string checkInDate;
std::string checkOutDate;
int numberOfNights;
double totalAmount;
BookingStatus status;
public:
Booking(...);
void calculateTotalAmount();
void setStatus(BookingStatus status);
void displayInfo() const;
};
Booking.cpp 中的两个要点:
- 构造即计价:构造函数中
status初始化为CONFIRMED,并立即调用calculateTotalAmount(),金额 =room->getPricePerNight() * numberOfNights; - 状态机:
BookingStatus的四个取值 CONFIRMED → CHECKED_IN → CHECKED_OUT / CANCELLED 定义了预订的完整生命周期(详见第三节)。
三、HotelManager:中央控制器与业务状态机
HotelManager.hpp 是系统的门面(Facade 式入口),内部维护三组容器和一个 ID 自增器:
std::vector<Room*> rooms;
std::vector<Guest*> guests;
std::vector<Booking*> bookings;
int bookingIdCounter; // 初始为 1,生成 "BK1"、"BK2" ...
3.1 预订流程(createBooking)
HotelManager.cpp 中 createBooking 的校验与装配逻辑:
Booking* HotelManager::createBooking(std::string guestId, std::string roomNumber,
std::string checkInDate, std::string checkOutDate,
int numberOfNights) {
Guest* guest = findGuest(guestId);
Room* room = findRoom(roomNumber);
if (!guest || !room || room->getStatus() != RoomStatus::AVAILABLE) {
return nullptr;
}
Booking* booking = new Booking(generateBookingId(), guest, room,
checkInDate, checkOutDate, numberOfNights);
bookings.push_back(booking);
return booking;
}
三个失败条件缺一不可:客人不存在、房间不存在、房间不可用。注意:本实现预订时不立即占用房间,房间状态仅在 check-in 时变更为 OCCUPIED;因此"可用性约束"发生在 check-in 而非 createBooking,这是该实现的明确取舍(真实系统中通常会在 createBooking 时就把房间标记为 BOOKED,以应对并发预订——这一点正是需求 6 指出的演进点)。
3.2 入住 / 退房 / 取消的状态机
三个核心操作构成严格的状态迁移矩阵:
| 操作 | 前置状态(Booking) | 后置状态(Booking) | 房间状态变化 |
|---|---|---|---|
checkIn |
CONFIRMED | CHECKED_IN | AVAILABLE → OCCUPIED |
checkOut |
CHECKED_IN | CHECKED_OUT | OCCUPIED → AVAILABLE |
cancelBooking |
CONFIRMED | CANCELLED | (本实现房间始终 AVAILABLE,见上) |
对应实现(HotelManager.cpp):
bool HotelManager::checkIn(std::string bookingId) {
Booking* booking = findBooking(bookingId);
if (!booking || booking->getStatus() != BookingStatus::CONFIRMED) return false;
booking->setStatus(BookingStatus::CHECKED_IN);
booking->getRoom()->setStatus(RoomStatus::OCCUPIED);
return true;
}
bool HotelManager::checkOut(std::string bookingId) {
Booking* booking = findBooking(bookingId);
if (!booking || booking->getStatus() != BookingStatus::CHECKED_IN) return false;
booking->setStatus(BookingStatus::CHECKED_OUT);
booking->getRoom()->setStatus(RoomStatus::AVAILABLE);
return true;
}
bool HotelManager::cancelBooking(std::string bookingId) {
Booking* booking = findBooking(bookingId);
if (!booking || booking->getStatus() != BookingStatus::CONFIRMED) return false;
booking->setStatus(BookingStatus::CANCELLED);
booking->getRoom()->setStatus(RoomStatus::AVAILABLE);
return true;
}
可以推断的设计意图:所有业务操作都要求"目标预订存在且处于允许的前置状态",非法迁移一律返回 false,从而保证数据一致性;findRoom / findGuest / findBooking 三个私有查找函数把"容器遍历 + 按 ID/房号匹配"的逻辑集中复用。
3.3 查询与报表能力
HotelManager.cpp 提供了 4 个查询接口,对应需求 7 的报表与分析:
displayAvailableRooms():遍历房间,仅打印AVAILABLE的房间;displayBookingHistory(guestId):按客人 ID 过滤其全部预订记录;displayAllGuests()/displayAllBookings():全量列表,用于前台运营管理。
此外 HotelManager.cpp 的析构函数统一 delete 所有 rooms / guests / bookings 指针,避免内存泄漏,是面试中值得强调的资源管理细节。
四、完整业务闭环:HotelDemo 演示走查
HotelDemo.cpp 串起了整个流程,可对照以下顺序运行理解:
- 创建
HotelManager实例; - 添加 3 间房(101 标准间 / 201 豪华间 / 301 套房);
- 添加 1 位客人
G001(John Doe); displayAvailableRooms()打印初始可用房间;createBooking("G001", "101", "2024-01-01", "2024-01-03", 2)创建 2 晚预订,成功后打印预订详情(含自动计算的$200.00总额);checkIn(bookingId)后再次displayAvailableRooms(),可看到 101 房已从列表消失(状态变为 OCCUPIED);checkOut(bookingId)释放房间;displayBookingHistory("G001")查看该客人的预订历史,此时状态为 Checked Out。
编译运行方式(在 solutions/cpp/hotelmanagementsystem/ 目录下):
g++ -std=c++17 HotelDemo.cpp HotelManager.cpp Booking.cpp Room.cpp Guest.cpp -o hotel_demo
./hotel_demo
五、类图与面试要点
仓库的 hotelmanagementsystem-class-diagram.png 提供了该系统的完整类图,直观呈现了 Guest、Room、Booking、HotelManager(HotelManagementSystem)之间的关联关系,非常适合面试讲解与博客配图使用。
面试与复习时可抓住以下要点:
- 关系建模:
Booking聚合Guest与Room(指针关联),HotelManager组合管理三类容器,是典型的"实体 + 门面控制器"分层; - 状态机设计:
BookingStatus四态与RoomStatus三态的双状态机联动是本题的考核核心,务必能画出状态迁移表(见 3.2 节); - 枚举即约束:
RoomType、RoomStatus、BookingStatus全部使用enum class,编译期类型安全; - 演进空间:需求 5(多支付方式)在文档层通过
Payment接口 + 具体实现类(CashPayment、CreditCardPayment 等)设计;需求 6(并发一致性)在本实现中体现为集中的HotelManager业务入口,可在该处引入互斥锁或数据库事务;需求 8(可扩展性)则依赖当前清晰的容器式数据结构。
从源码结构看,本实现有意保持"单文件、无外部依赖、可直接编译运行"的教学形态,非常适合作为 LLD 面试前的快速复习模板——先读懂状态机,再动手重写一遍 HotelManager 的四个核心操作,即可达到面试手写要求。
