ARTICLE DETAIL

资讯详情

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

设计一个类 Uber 的网约车系统(Ride-Sharing Service):需求、领域建模与多设计模式实现解析

设计一个类 Uber 的网约车系统(Ride-Sharing Service):需求、领域建模与多设计模式实现解析 示例工程【免费下载链接】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 仓库中的 ride-sharing-service 问题规格书 为核心骨架结合仓库内 Java、Go、C、C#、Python 五套参考实现系统讲解如何为乘客发起行程、司机接单履约的网约车场景完成低层设计LLD。读完本文你将掌握网约车系统的需求拆解方法、核心领域模型Passenger / Driver / Ride / Location / Payment / RideService的职责划分以及如何用 Singleton、Strategy、Builder、State、Observer 五种设计模式与并发安全数据结构构建一个可扩展、可演示的核心服务。需求分析八条核心功能规格网约车系统的功能需求可以收敛为以下八条它们是领域建模与类设计的事实来源行程双向流转乘客可以发起行程请求司机可以接受并履约该行程。行程参数可配置乘客需能指定上车点pickup、目的地destination以及想要的车型ride type如普通车 regular、高级车 premium。司机自主决策司机应能看到可用行程请求并选择接受或拒绝。匹配策略系统应基于距离和其他因素将行程请求匹配给可用司机。动态计价系统应根据距离、时间与车型计算每次行程的费用fare。支付处理系统应处理乘客与司机之间的支付与交易。实时追踪与通知系统应对进行中的行程提供实时追踪并向乘客、司机推送行程状态更新。并发与一致性系统应能处理并发请求并保证数据一致性。对应到仓库的 Java 参考实现solutions/java/src/ridesharingservice/需求 1~3 由RideSharingService的requestRide/acceptRide流程承担需求 4 由DriverMatchingStrategy承担需求 5 由PricingStrategy承担需求 7 由 Observer 回调承担需求 8 由ConcurrentHashMap等并发容器承担。各需求的实现落点将在后文逐节展开。说明原文档提到的 Passenger、Ride、Payment 等类在 Java 实现中分别对应Rider、Trip与计价/支付相关逻辑processPayment在规格书中被明确标注为占位方法示意后续可接入真实支付网关文章以规格书类名为准同时标注实现中的对应类型。领域模型核心类、接口与枚举从类图上可以读出系统的静态结构UML 类图 给出了全套实体的职责边界Ride-Sharing Service UML 类图Passenger乘客与 RiderPassenger类代表网约车系统中的乘客包含 ID、姓名、联系方式与当前位置等属性。Java 实现中以 Rider 形式落在observer包下其抽象基类 User 统一提供id由UUID.randomUUID()生成的全局唯一 IDname/contact姓名与联系方式tripHistory历史行程列表配套addTripToHistory(Trip)与getTripHistory()方法用于行程结算与订单历史查询。User同时实现了TripObserver接口意味着乘客和司机都具备接收行程状态通知的能力见Observer 模式一节。Driver司机Driver类代表司机在乘客字段之外还持有车型、车牌、当前位置与工作状态。Java 实现 Driver 的关键设计点默认状态为OFFLINE需要显式setStatus(DriverStatus.ONLINE)上线后才可能被匹配到持有Vehicle对象车牌号、品牌车型、RideType车型是匹配与计价的关键输入重写onUpdate(Trip)当行程处于REQUESTED状态时向司机输出有新的行程可接的通知——这是需求 7通知司机的简化实现。司机状态枚举 DriverStatus 的取值OFFLINE / ONLINE / IN_TRIP直接驱动匹配过滤逻辑只有ONLINE的司机才进入候选集司机接单后置为IN_TRIP行程结束后复位为ONLINE。Ride / Trip行程Ride类代表一个乘客发起、司机接受的行程包含 ID、乘客、司机、出发地、目的地、状态与费用。Java 实现为 Trip其不可变字段通过内部TripBuilder构建字段说明id行程唯一 IDUUIDrider发起行程的乘客driver接受行程的司机初始为空接单后赋值pickupLocation/dropoffLocation上车点 / 目的地fare预估费用status行程状态TripStatus枚举Trip的状态流转不采用散落的if/else而是交给状态对象初始状态为RequestedState之后经assign→start→end在 AssignedState、InProgressState、CompletedState 之间切换每个状态对象通过setState/setStatus/setDriver三个 protected 方法回写行程。Location地理位置Location类以经纬度表示地理位置。Java 实现 Location 提供distanceTo(Location)方法用于匹配与计价public double distanceTo(Location other) { double dx this.latitude - other.latitude; double dy this.longitude - other.longitude; return Math.sqrt(dx * dx dy * dy); // Euclidean for simplicity }需要注意示例实现为演示简单采用欧几里得距离近似生产环境应替换为 Haversine 公式计算球面距离并配合地理索引如 GeoHash、四叉树支撑大规模司机检索。Payment支付Payment类代表行程支付规格书中定义的属性为 ID、对应行程、金额与支付状态。对应实现中计价在PricingStrategy.calculateFare完成而processPayment被明确标注为占位方法示意计算费用后接入支付网关完成交易的扩展点——真实系统中还需引入支付状态机PENDING / SUCCEEDED / FAILED / REFUNDED与对账机制。枚举RideType 与 TripStatusRideTypeSEDAN、SUV、AUTO三种车型对应规格书中的 regular / premium 分级驱动匹配过滤与计价费率TripStatusREQUESTED、ASSIGNED、IN_PROGRESS、COMPLETED等状态是通知回调中的关键上下文。RideService单例门面与并发安全RideService是管理网约车服务的核心门面规格书明确要求它遵循Singleton 模式保证全局唯一实例。Java 实现 RideSharingService 采用经典的双重校验单例private static volatile RideSharingService instance; public static synchronized RideSharingService getInstance() { if (instance null) { instance new RideSharingService(); } return instance; }volatile与synchronized的组合保证了多线程环境下单例的唯一性与可见性。内部数据全部落在并发容器上以支撑需求 8并发请求与数据一致性private final MapString, Rider riders new ConcurrentHashMap(); private final MapString, Driver drivers new ConcurrentHashMap(); private final MapString, Trip trips new ConcurrentHashMap();规格书中提到的ConcurrentHashMap与ConcurrentLinkedQueue在此处体现为ConcurrentHashMap保证按 ID 的 O(1) 读写与线程安全ConcurrentLinkedQueue在规格书版本中用于承载待派单的行程请求队列保证多个司机并发拉取请求时 FIFO 与一致性。生命周期方法一览RideService 对外暴露的核心方法覆盖了行程全生命周期方法职责关键行为registerRider(name, contact)注册乘客生成 UUID写入riders并发 MapregisterDriver(name, contact, vehicle, location)注册司机记录车辆与初始位置默认OFFLINErequestRide(riderId, pickup, dropoff, rideType)发起行程依次执行匹配、计价、建 Trip、通知附近司机acceptRide(driverId, tripId)司机接单司机置为IN_TRIP行程状态推进到 ASSIGNEDstartTrip(tripId)开始行程状态机推进到 IN_PROGRESS 并广播通知endTrip(tripId)结束行程结算历史、司机复位ONLINE并更新位置requestRide的调用链完整呈现了需求 4、5、7 的协作见 RideSharingService.java#L56-L95从riders中按 ID 取出乘客不存在则抛出NoSuchElementException调用driverMatchingStrategy.findDrivers(...)找出候选司机候选为空则提示稍后再试调用pricingStrategy.calculateFare(...)计算预估费用用 Builder 构建Trip并存入trips遍历候选司机调用driver.onUpdate(trip)模拟向附近司机推送新行程通知。endTrip中还体现了状态收尾逻辑司机位置更新为目的地、状态复位ONLINE并将行程分别写入司机与乘客的历史列表RideSharingService.java#L117-L134。五种设计模式的落点从实现源码可以确认这套网约车系统是多设计模式协作的典型样例恰好覆盖 LLD 面试的高频考点。Strategy匹配与计价可替换匹配与计价都通过策略接口解耦运行期可整体替换DriverMatchingStrategy 定义findDrivers(ListDriver, Location, RideType)默认实现 NearestDriverMatchingStrategy 采用过滤 排序流水线只保留ONLINE司机 → 车型与请求一致 → 距离不超过MAX_DISTANCE_KM 5.0→ 按距离升序排列PricingStrategy 定义calculateFare(Location, Location, RideType)两个现成实现分别是VehicleBasedPricingStrategy按车型计价private static final double BASE_FARE 2.50; private static final MapRideType, Double RATE_PER_KM Map.of( RideType.SEDAN, 1.50, RideType.SUV, 2.00, RideType.AUTO, 1.00 ); // fare BASE_FARE RATE_PER_KM.get(rideType) * pickup.distanceTo(dropoff)FlatRatePricingStrategy统一费率BASE_FARE 5.0每公里FLAT_RATE 1.5不区分车型。两个实现恰好演示了计价规则变化时只需新增一个策略类无需改动服务门面这正是策略模式的核心收益。规格书提及的基于距离、时间和车型的完整计价公式可在该扩展点上补充时长费用与高峰动态系数。BuilderTrip 的不可变构建Trip的字段众多且含必填约束实现采用 TripBuilder内部静态类保证构建顺序与校验Trip trip new Trip.TripBuilder() .withRider(rider) .withPickupLocation(pickup) .withDropoffLocation(dropoff) .withFare(fare) .build();build()内对rider、pickupLocation、dropoffLocation做非空校验缺失即抛IllegalStateException从构建阶段就杜绝半成品行程进入系统。State行程状态机Trip持有TripState currentState所有状态转移assign / start / end都委托给当前状态对象完成后续新增取消或退款状态只需新增状态类无需改动Trip主流程——对应规格书中发起、接受、开始、完成、取消行程的完整生命周期扩展点。Observer状态更新通知Trip维护ListTripObserver在assignDriver、startTrip、endTrip后调用notifyObservers()广播更新Driver和User均实现了TripObserver接口通过onUpdate(Trip)感知状态变化如收到可接单提醒。这正是需求 7实时通知乘客与司机的源码级实现生产环境可把本地回调替换为 WebSocket / 推送网关。Singleton全局唯一服务实例如前一节所述RideSharingService以双重校验锁保证全局唯一避免多个服务实例导致司机、乘客、行程数据分裂。端到端演示RideSharingServiceDemo演示入口 RideSharingServiceDemo 完整走通注册 → 上线 → 发单 → 匹配 → 接单 → 开始 → 结束 → 查历史全流程其关键分支可以帮助理解匹配与计价的实际行为获取单例注入NearestDriverMatchingStrategy与VehicleBasedPricingStrategy注册乘客 Alice 与三位司机 Bob丰田普锐斯 SEDAN位于 (1.0, 1.0)、Charlie本田 CRV SUV位于 (2.0, 2.0)、David本田 CRV SEDAN位于 (1.2, 1.2)三位司机全部上线随后将 David 挪到 (10.0, 10.0)——故意让它超出 5km 匹配半径Alice 从 (0.0, 0.0) 到 (5.0, 5.0) 请求 SEDAN匹配结果BobSEDAN、距 pickup 约 1.41 且在线命中Charlie 因车型是 SUV 被过滤David 因距离 14.1km 超过MAX_DISTANCE_KM被过滤——一次演示同时验证了车型过滤与距离过滤两条规则Bob 接单 → 开始行程 → 结束行程随后打印 Alice 与 Bob 的行程历史第二个场景中Harry 请求 SUV匹配结果只剩 Charlie验证车型约束再次生效。运行该演示即可在控制台逐行观察匹配日志、预估费用、司机接单通知与状态流转输出是理解整个调用链最直观的入口。Go 版本入口位于 solutions/golang/ridesharingservice/ride_sharing_service_demo.go其 README 记录了运行方式可用作对照学习。多语言实现概览规格书为同一需求提供了五套语言实现便于横向对比语言特性与设计表达Javasolutions/java/src/ridesharingservice/ —— 本文主要分析的实现设计模式最完整Singleton Strategy Builder State ObserverPythonsolutions/python/ridesharingservice/ —— 以设计笔记md为主可作为概念对照Csolutions/cpp/ridesharingservice/ —— 头文件 实现分离的组织方式C#solutions/csharp/ridesharingservice/ —— 属性、事件等 C# 惯用语法Gosolutions/golang/ridesharingservice/ —— 以接口 结构体组合的方式表达策略与状态ride_service.go与ride.go可对照 Java 版本阅读。扩展方向与面试要点基于规格书与源码可归纳出本设计面向面试/迭代的核心扩展点真实地理计算将Location.distanceTo的欧几里得近似替换为 Haversine 球面距离并发队列规格书中的ConcurrentLinkedQueue可进一步演化为带优先级如 VIP 乘客的派单队列支付落地将占位的processPayment接入支付网关并补充支付状态机与幂等幂等键 tripId 重试次数取消与重派状态机新增CANCELLED状态与超时未接单后的重派逻辑匹配扩展在DriverMatchingStrategy下新增评分优先顺路优先等策略验证策略模式的可扩展性。一句话总结网约车系统的 LLD 核心在于把发单—匹配—计价—接单—履约—通知—支付这条业务链路拆解为清晰的实体职责并用 Singleton 保证服务唯一、Strategy 隔离匹配/计价变化、Builder 约束行程构建、State 管理状态流转、Observer 打通实时通知最终以并发容器保障数据一致性——这套组合拳正是面试官期待的完整答案。赞分享示例工程【免费下载链接】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点击查看免费下载相关推荐用 C 设计一个类 Amazon 的在线购物系统需求分析、类设计与设计模式实战用 C 设计一个类 Amazon 的在线购物系统需求分析、类设计与设计模式实战 本篇技术指南基于 awesome low level design 仓库中 C示例工程用 C 设计一个类似 Facebook 的社交网络系统从需求建模到设计模式落地用 C 设计一个类似 Facebook 的社交网络系统从需求建模到设计模式落地 社交网络Social Network是低层设计LLD面试与系统设计练习示例工程SeaTunnel Cloudberry 源连接器使用指南基于 PostgreSQL JDBC 驱动的高性能数据读取SeaTunnel Cloudberry 源连接器使用指南基于 PostgreSQL JDBC 驱动的高性能数据读取 导读 CloudberryApache示例工程上一篇PyPTO Pro 量化与反量化 API 实战指南quant / dequant 的语义、参数与源码实现下一篇ice/plugin-intl 国际化插件使用指南基于 react-intl 为 ice.js 应用接入多语言创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表