ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

使用 TypeScript 设计停车场系统:从需求分析到单例模式实现(awesome-low-level-design 实战)

使用 TypeScript 设计停车场系统:从需求分析到单例模式实现(awesome-low-level-design 实战) 示例工程【免费下载链接】awesome-low-level-designLearn Low Level Design (LLD) and prepare for interviews using free resources.项目地址https://gitcode.com/GitHub_Trending/aw/awesome-low-level-design点击查看免费下载导读本文以 awesome-low-level-design 仓库中的 TypeScript 停车场系统实现 为核心完整讲解如何用 TypeScript 的 OOP 能力类、接口、抽象类、枚举/联合类型设计一个多楼层、多车型、支持实时余位查询的停车场系统。你将掌握需求拆解、类职责划分、Singleton 单例模式的落地方式以及基于抽象基类实现 Car/Bike/Truck 多态车型的完整编码套路可直接复用到面试与真实业务中。一、需求分析先把问题拆清楚原文档readme.md给出了一套清晰的 LLD 需求核心要点如下停车场由多个楼层Levels/Floor构成每个楼层拥有可配置数量的停车位系统需要支持多种车型Car小汽车、Motorcycle摩托车、Truck卡车——在仓库实现中以Car、Bike、Truck出现每个停车位Parking Spot只支持特定车型系统必须能为入场车辆分配车位、车辆出场时释放车位、实时跟踪并汇报余位需要支持多个入口/出口 并发访问必要时模拟多线程行为设计必须体现 TypeScript 的 OOP 原则类、接口、抽象类、枚举。对应的问题背景还可参考仓库中的 parking-lot.md其中补充了 UML 类图class-diagrams/parkinglot-class-diagram.png与多语言实现索引本文聚焦 TypeScript 版本。该图为仓库统一绘制的 Parking Lot 类图展示了 ParkingLot / ParkingFloor / ParkingSpot / Vehicle 之间的组合与继承关系与 TypeScript 实现一一对应。二、项目结构模块划分与文件职责TypeScript 实现位于solutions/typescript/src/ParkingLot/共 6 个文件每个文件承担一个明确职责文件职责types.ts定义车型联合类型carType等价于枚举Vehicle.tsVehicle接口 AbstractVehicle抽象基类 Car/Bike/Truck 具体类Spot.tsParkingSpot车位类可用性、车型匹配、停车/取车Floor.tsParkingFloor楼层类车位集合、按车型汇报余位、查找可停车位ParkingLot.tsParkingLot主类Singleton 单例、停车/取车/余位汇报Main.ts演示脚本搭建两楼层停车场并演示全流程这种一个文件一个核心类的划分方式正是 LLD 面试中展示高内聚、低耦合设计的经典做法。三、类型设计用联合类型替代枚举原文档要求enums体现TypeScript 中除了原生enum更现代的做法是字符串字面量联合类型。仓库在 types.ts 中选择了联合类型export type carType Car | Truck | Bike;只有Car、Truck、Bike三个合法值编译期即可拦截拼写错误配合 TS 的字面量推断在Vehicle子类与ParkingSpot之间天然保持类型一致无需运行时校验如果面试官期望enum也可以等价替换为enum carType { Car, Truck, Bike }联合类型与枚举都满足原文档枚举/类型安全的要求。四、车辆模型接口 抽象类 多态Vehicle.ts 完整体现了原文档要求的三个 OOP 要素interface Vehicle { getNumber(): string; getType(): carType; } abstract class AbstractVehicle implements Vehicle { protected number: string; protected type: carType; constructor(number: string, type: carType) { this.number number; this.type type; } getNumber(): string { return this.number; } getType(): carType { return this.type; } } class Car extends AbstractVehicle { constructor(number: string) { super(number, Car); } } class Bike extends AbstractVehicle { constructor(number: string) { super(number, Bike); } } class Truck extends AbstractVehicle { constructor(number: string) { super(number, Truck); } }设计要点接口Vehicle声明契约任何车辆必须能回答车牌号是什么、车型是什么抽象基类AbstractVehicle收敛公共字段与公共实现子类只需传入自己的车型字面量杜绝重复代码多态停车场所有逻辑都面向AbstractVehicle编程见下文canFit/findAvailableSpot新增车型只需新增一个子类符合开闭原则。五、车位模型类型匹配 可用性守卫Spot.ts 定义了ParkingSpotclass ParkingSpot { spotName: string; protected type: carType; protected parkedCar: AbstractVehicle | null; get isAvailable(): boolean { return !this.parkedCar; } canFit(car: AbstractVehicle): boolean { return car.getType() this.type; } parkCar(car: AbstractVehicle): void { if (!this.isAvailable) { throw new Error(Spot already occupied.); } if (!this.canFit(car)) { throw new Error(${car.getNumber()} cant fit in this spot.); } this.parkedCar car; } unparkCar(): void { this.parkedCar null; } }实现细节值得在面试中展开isAvailable使用 getter通过parkedCar是否为null动态推导而不是维护一个容易失步的 boolean 标志双重守卫parkCar先检查已被占用再检查车型不匹配两条路径都会抛出明确错误信息如car-1 cant fit in this spot.保证业务约束不被绕过canFit即多态匹配car.getType() this.type用联合类型做精确相等判断运行时零成本。六、楼层模型余位报表与车位查找Floor.ts 的ParkingFloor负责楼层内的聚合逻辑type carWiseSpot RecordcarType, ParkingSpot[]; reportAvailability(): carWiseSpot { const carWiseSpots: carWiseSpot { Car: [], Truck: [], Bike: [] }; for (let spot of this.spots) { if (spot.isAvailable) { carWiseSpots[spot.getSpotType].push(spot); } } return carWiseSpots; } findAvailableSpot(vehicle: AbstractVehicle): ParkingSpot | null { for (let spot of this.spots) { if (spot.canFit(vehicle) spot.isAvailable) { return spot; } } return null; }reportAvailability()返回按车型分组的余位映射RecordcarType, ParkingSpot[]直接满足实时跟踪并汇报余位的需求且返回结构天然适合序列化给前端或接口调用方findAvailableSpot()采用先到先得顺序扫描同时满足canFit与isAvailable即分配返回null表示本楼层无匹配空位由上层决定是否继续搜索下一层。七、停车场主类Singleton 单例模式的落地原文档要求 Singleton: Ensure only one instance of the ParkingLot exists。仓库在 ParkingLot.ts 中给出了经典实现class ParkingLot { private static instance: ParkingLot; private floors: ParkingFloor[]; name: string; protected carSpotMap: Recordstring, ParkingSpot; constructor(name: string) { this.name name; this.floors []; this.carSpotMap {}; } static getInstance(name: string Default): ParkingLot { if (!ParkingLot.instance) { ParkingLot.instance new ParkingLot(name); } return ParkingLot.instance; } parkCar(vehicle: AbstractVehicle): void { for (let floor of this.floors) { const availableSpot floor.findAvailableSpot(vehicle); if (availableSpot) { availableSpot.parkCar(vehicle); console.log(${vehicle.getNumber()} parked on ${availableSpot.spotName}); return; } } } unparkCar(vehicle: AbstractVehicle): void { const spot this.carSpotMap[vehicle.getNumber()]; if (!spot) { throw new Error(${vehicle.getNumber()} is not Parked.); } spot.unparkCar(); delete this.carSpotMap[vehicle.getNumber()]; } spotAvailability(): void { for (let floor of this.floors) { console.log(${floor.floor} : , floor.reportAvailability()); } } addFloor(floor: ParkingFloor): void { this.floors.push(floor); } }几个值得强调的工程细节懒加载单例首次调用getInstance()时才构造实例之后无论调用多少次都返回同一对象name带默认值Default保证无参调用也可用carSpotMap以车牌号为键parkCar时由车位在内部parkCar完成实际占用但注意仓库当前实现中carSpotMap的写入与parkCar的同步、以及按车牌取回映射的完整闭环是读者可以继续推敲和补全的扩展点例如在parkCar成功后登记this.carSpotMap[vehicle.getNumber()] availableSpotunparkCar的防御式校验若车辆未停入不在映射中直接抛出is not Parked错误避免对空位执行释放操作分配策略parkCar从floors数组首层开始逐层调用findAvailableSpot体现最低楼层优先的朴素策略若所有楼层均无匹配车位当前实现静默返回读者可扩展为返回null或抛出停车场已满错误。八、Main.ts 演示脚本端到端跑通仓库提供了可直接运行的演示脚本 Main.tsimport ParkingLot from ./ParkingLot; import ParkingFloor from ./Floor; import ParkingSpot from ./Spot; import { Car } from ./Vehicle; const parkYourCar ParkingLot.getInstance(ParkYourCar); console.log(parkYourCar.name); const car1 new Car(car-1); const car2 new Car(car-2); const car3 new Car(car-3); const car4 new Car(car-4); const a1 new ParkingSpot(a-1, Car); const a3 new ParkingSpot(a-3, Bike); const a4 new ParkingSpot(a-4, Car); const f1 new ParkingFloor(1); f1.addSpot(a1); f1.addSpot(a3); f1.addSpot(a4); const b1 new ParkingSpot(b-1, Car); const b2 new ParkingSpot(b-2, Truck); const b4 new ParkingSpot(b-4, Car); const f2 new ParkingFloor(2); f2.addSpot(b1); f2.addSpot(b2); f2.addSpot(b4); parkYourCar.addFloor(f1); parkYourCar.addFloor(f2); parkYourCar.spotAvailability(); parkYourCar.parkCar(car1); parkYourCar.parkCar(car2); parkYourCar.spotAvailability();脚本演示了完整流程通过ParkingLot.getInstance(ParkYourCar)获取唯一实例并验证name构造 4 辆Car为楼层 1 添加 Car/Bike/Car 三个车位为楼层 2 添加 Car/Truck/Car 三个车位注释掉的a-2、b-3、b-5等行展示了按需增减车位的可配置性通过addFloor将楼层挂载到停车场调用spotAvailability()打印停车前的实时余位按车型分组parkCar(car1)、parkCar(car2)依次停车再调用spotAvailability()观察余位变化。注意当前脚本中Truck与Bike的具体车辆实例未真正创建若要完整验证多车型匹配可仿照new Car(car-1)追加new Truck(truck-1)、new Bike(bike-1)再调用parkCar——这正是原文档Factory(optional): 动态创建车辆提到的扩展方向。运行方式项目根目录位于 solutions/typescript其 package.json 提供了tsx直接执行 TS 的能力devDependencies中声明了tsx安装与运行命令如下npm i npx tsx src/ParkingLot/Main.ts若要运行整个 solutions 的入口脚本可参考 README.mdnpm i npx tsx src/lldrunner.ts九、并发与扩展方向原文档要求的兑现与延伸原文档明确要求支持多个入口/出口的并发访问处理必要时模拟多线程并提到三种可选设计模式1. 并发安全Mutual ExclusionTypeScript 运行在单线程事件循环之上但异步竞态依然存在——例如两个并发协程同时在carSpotMap中登记同一车牌、或同一车位被两次分配。原文档给出的思路是Mutex 模拟用Promise链或锁变量如private isLocked falseasync acquire()/release()把parkCar/unparkCar的关键区串行化Promise 控制将parkCar改为async方法通过一个内部队列保证分配—登记操作原子完成从源码结构看当前 ParkingLot.ts 的parkCar与unparkCar均为同步方法因此并发防护是原文档点名的明确扩展点读者可基于上述思路自行补全。2. Factory 模式可选原文档建议用 Factory 动态创建车辆。仓库 Vehicle.ts 目前由调用方直接new Car(...)可平滑演进为function createVehicle(number: string, type: carType): AbstractVehicle { switch (type) { case Car: return new Car(number); case Bike: return new Bike(number); case Truck: return new Truck(number); } }这样调用方只需传车型字符串不再直接依赖具体子类。3. Observer 模式可选原文档建议车位可用时通知用户。可将ParkingSpot扩展为被观察者notify在unparkCar后触发订阅者如等待车位的大车司机在isAvailable翻转时收到回调。该模式在仓库当前实现中未落地属于文档标注的可选增强。十、面试答辩要点速查基于本文实现在 LLD 面试中可按以下顺序自检问题答案要点源码证据为什么用 Singleton停车场全局唯一getInstance懒加载 静态实例ParkingLot.ts车型如何保证类型安全字符串字面量联合类型carTypetypes.ts车位如何匹配车型canFit比较getType() this.typeSpot.ts余位如何实时汇报reportAvailability按车型分组返回Floor.ts扩展新车型要改哪里只需新增AbstractVehicle子类 扩展联合类型Vehicle.ts并发怎么处理Mutex 模拟 / Promise 队列串行化关键区扩展点原文档需求第 5 点结语本文以 solutions/typescript/src/ParkingLot/readme.md 的需求为骨架对照仓库源码逐层剖析了types.ts → Vehicle → Spot → Floor → ParkingLot → Main的完整调用链覆盖了类型安全、多态、Singleton 单例、实时余位报表四大核心设计点并指明了并发、Factory、Observer 三个文档标注的扩展方向。直接运行 Main.ts 即可看到真实输出若想对比其他语言实现仓库还提供了 Java、C、C#、Go 等多个版本供横向参考。赞分享示例工程【免费下载链接】awesome-low-level-designLearn Low Level Design (LLD) and prepare for interviews using free resources.项目地址https://gitcode.com/GitHub_Trending/aw/awesome-low-level-design点击查看免费下载相关推荐awesome-low-level-design黑板架构从停车场系统看设计模式实战awesome low level design黑板架构从停车场系统看设计模式实战 架构全景从需求到代码的桥梁 在软件开发中Low Level Desig示例工程awesome-low-level-design清洁架构从理论到停车场系统实战awesome low level design清洁架构从理论到停车场系统实战 什么是清洁架构Clean Architecture 清洁架构Clean示例工程基于 C 实现 Uber 式 Ride-Sharing Service从需求分析到类设计实战awesome-low-level-design基于 C 实现 Uber 式 Ride Sharing Service从需求分析到类设计实战awesome low level design 导读 本示例工程上一篇Karmada 成员集群资源缓存实战基于 karmada-search 与 ResourceRegistry 构建统一跨集群资源视图下一篇Gutenberg Dashboard 初始化模块解析wordpress/dashboard-init 的实体注册与图标解析机制创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表