单步电梯调度系统总结

前言:
完成三次电梯大作业之后,我将对题目重新进行设计与分析,对知识点、题型难度、题量等进行总结,以及提出踩坑心得和改进建议。
一、知识点
1.基础知识点
(1)基本语法
标识符与关键字:使用static final定义常量(如DirectionCode中的ASCENDING、StatusCode中的STATIONARY);
基本数据类型:int(楼层、方向)、boolean(是否内部呼叫)、String(运行状态),以及包装类Integer(用于空值判断);
输入输出:通过Scanner读取控制台输入,System.out.println输出电梯状态,覆盖Scanner的nextLine()、trim()、parseInt()等常用方法;
运算符:三元运算符(calculateDirection中判断方向)、算术运算符(currentLevel += 1/-1)、逻辑运算符(&&/||在requiresStop中)。
(2)流程控制
分支结构:if-else(判断呼叫类型、移动方向、是否需要停层)、多重if(determineNextTarget中优先级判断);
循环结构:
while循环(电梯主运行逻辑startOperation、楼层移动navigateToDestination);
while (!队列.isEmpty())(遍历处理队列元素,如addExternalDestinationsToInterior);
循环终止:通过break(目标为空时退出主循环)、条件判断(inputLine.equalsIgnoreCase("end")终止输入)。
(3)数组与字符串处理
字符串操作:replace()(移除<>)、split()(分割外部请求的源 / 目的楼层)、trim()(去除输入空格)、equalsIgnoreCase()(忽略大;
2.面向对象核心设计
(1)类与对象
CallRequest:封装电梯呼叫请求的属性(目标楼层、方向、是否内部呼叫);
DirectionCode/StatusCode:常量类,封装方向 / 状态的固定值(枚举思想的简化实现);
Main:电梯核心控制类,封装状态、队列、业务方法;
对象实例化:
构造方法创建CallRequest;
Main elevatorSystem = new Main(minLevel, maxLevel)创建电梯实例。
(2) 封装
访问修饰符:
私有属性:Main中的currentLevel、interiorCalls等用private修饰,避免直接暴露;
公共方法:registerCall、startOperation等public方法提供外部访问入口;
私有方法:navigateToDestination、calculateDirection等private方法封装内部逻辑,对外隐藏实现;
常量封装:DirectionCode/StatusCode用static final定义常量,集中管理、避免硬编码。
3. 构造方法重载
CallRequest提供两个构造方法,适配 “内部呼叫”和 “外部呼叫”的不同初始化场景
4. 静态成员
static final常量:DirectionCode中的ASCENDING、StatusCode中的STATIONARY,属于类级别的常量,无需实例化即可访问。
5.数据结构应用
队列
容器选择:使用LinkedList实现Queue接口(Queue interiorCalls = new LinkedList<>()),利用队列 “先进先出” 特性处理电梯呼叫请求;
核心方法:offer()(添加元素)、poll()(移除并返回队首)、peek()(查看队首不删除)、isEmpty()(判断是否为空),完美适配电梯请求的 “先来先处理” 逻辑。

总结
这份代码是 Java 基础 + 面向对象 + 数据结构的综合应用,核心考点包括:
OOP 的封装、类的拆分、构造方法重载;
队列(LinkedList)的 FIFO 特性与核心方法;
业务逻辑与算法
电梯调度算法:优先处理同方向请求,顺路停靠逻辑,方向切换规则:同方向请求处理完毕后切换反向。
请求管理:区分内部请求和外部请求,请求有效性校验。
状态与流程控制:电梯状态:静止、移动中、方向:上行、下行、空闲的切换管理,楼层移动与停靠的流程控制:移动→检查停靠→开门→处理请求→关门。
二、题量与题型特点
1、题量
三题均为单道编程题,每题独立完成电梯模拟功能
2、题型特点
递进式难度:三题基于同一核心场景,后一题在前者基础上增加约束或修改规则,难度逐步提升:
第 1 题:基础功能实现,仅需处理有效请求,核心是调度算法和队列管理。
第 2 题:增加请求校验逻辑:无效楼层过滤、重复请求去重,强化输入处理的健壮性。
第 3 题:修改外部请求格式(从 “楼层 + 方向” 改为 “源楼层 + 目的楼层”),增加 “外部请求处理后将目的楼层加入内部队列” 的联动逻辑,复杂度最高。
输出格式严格:需精准匹配楼层移动、停靠开门 / 关门的输出格式,对流程控制的细节要求高。
3、难度分析
第 1 题
难度:
简单 - 中等
核心难点:

  1. 同方向优先调度算法的实现;2. 内部 / 外部请求队列的协同处理;3. 移动与停靠的流程同步。
    第 2 题
    难度:中等
    核心难点
  2. 重复请求的判断与过滤;2. 无效楼层的快速校验;3. 不破坏原有调度逻辑的前提下增加校验功能。
    第 3 题
    难度:中等 - 较难
    核心难点:
  3. 外部请求格式修改后的解析逻辑;2. 外部请求处理与内部队列的联动;3. 调度算法适配新请求模式。
    整体难度总结
    基础门槛:掌握 Java 队列操作、面向对象封装、字符串解析,难度较低。
    核心挑战:电梯调度算法的逻辑严谨性、请求处理的边界条件,难度中等。
    进阶要求:多规则叠加时的代码扩展性,难度中等偏上。

二、设计与分析
第一题:
代码质量度量图:
image

解释
1、基础信息
文件路径:D:\test2\,项目名222,文件共 328 行代码、包含 185 条语句。
代码结构:2 个类 / 接口,每个类平均 7 个方法,每个方法平均约 10.29 条语句。
2、核心复杂度指标(重点)
最复杂方法:Main.handleCurrentLevelRequests()(复杂度 19,位于第 141 行),是代码中逻辑最复杂的方法;
整体复杂度:最大复杂度 19,平均复杂度 5.79(属于较高复杂度,代码逻辑较复杂);
注释与分支:带注释的代码行仅占 2.4%(注释率极低),分支语句占比 24.9%(条件判断较多)。
3、方法复杂度详情
(格式:方法名 → 复杂度,语句数,最大嵌套深度,调用数)
高复杂度方法:
Main.handleCurrentLevelRequests():19, 18, 4, 20(复杂度最高)
Main.findAscendingTarget()/findDescendingTarget():8, 16, 5, 4
Main.requiresStop():12, 11, 4, 10
低复杂度方法:
Main.calculateDirection()/openDoors()等:复杂度仅 1(逻辑简单)
4、代码块嵌套深度分布
代码块嵌套深度对应的语句数:
深度 2:57 条(最多)
深度 3:49 条
深度 4:30 条
深度 5:8 条
最大深度 5(嵌套层次偏深)
5、可视化信息
雷达图:显示 “平均复杂度”“最大复杂度” 等指标明显偏高;
柱状图:语句集中在深度 2-3,说明代码嵌套较多。
整体来看,这个文件逻辑复杂(平均复杂度 5.79)、嵌套层次深、注释率极低,维护成本较高;其中handleCurrentLevelRequests()是核心复杂方法,需要重点优化。
类图:
image

解释

  1. 类的职责拆分
    Main类(电梯核心控制类):
    封装电梯状态:currentLevel(当前楼层)、currentDirection(运行方向)、operationalStatus(运行状态)等私有属性;
    管理请求队列:interiorCalls(内部呼叫队列)、exteriorCalls(外部呼叫队列);
    提供核心方法:构造方法初始化电梯、registerCall注册呼叫、startOperation启动运行,以及navigateToDestination(导航到目标楼层)、requiresStop(判断是否停层)等私有业务方法(用-标注)。
    CallRequest类(请求实体类):
    封装呼叫信息:floorNumber(目标楼层)、moveDirection(移动方向)、isInsideCall(是否内部呼叫);
    重载构造方法:适配 “内部呼叫”(仅目标楼层)和 “外部呼叫”(呼叫楼层 + 方向)两种场景。
    StatusCode/DirectionCode类(常量类):
    用static final定义固定值(如STATIONARY="STOPPED"、ASCENDING=1),集中管理状态 / 方向的常量,避免硬编码。
  2. 类之间的关系
    Main依赖StatusCode:Main的operationalStatus属性使用StatusCode的常量,类图中用虚线箭头表示 “依赖” 关系;
    Main/CallRequest依赖DirectionCode:Main的currentDirection、CallRequest的moveDirection均使用DirectionCode的常量,同样是 “依赖” 关系;
    Main聚合CallRequest:Main的interiorCalls/exteriorCalls是Queue,类图中隐含 “聚合” 关系(Main包含CallRequest对象)。

心得:从类图看面向对象设计的优劣

  1. 设计优点:符合基础 OOP 原则
    单一职责:类的职责拆分清晰 ——Main管控制逻辑、CallRequest封装请求数据、StatusCode/DirectionCode管理常量,每个类只做一件事;
    封装性:Main的核心属性(如currentLevel)设为私有,通过方法(如navigateToDestination)间接修改,隐藏内部实现细节;
    常量集中管理:将状态 / 方向的固定值抽为独立类,避免代码中散列的硬编码,提升可维护性;
    构造方法重载:CallRequest用不同构造方法适配不同呼叫场景,满足 “多态” 的基础设计。
  2. 设计不足:可优化的方向
    依赖关系的合理性:Main直接依赖StatusCode/DirectionCode的常量,若后期状态 / 方向扩展(如新增 “故障状态”),需修改Main代码,违背 “开闭原则”;
    聚合关系的显式表达:类图中未明确标注Main与CallRequest的聚合关系(仅通过属性隐含),若团队协作,易忽略 “Main包含CallRequest” 的核心关联;
    方法的访问修饰符:Main的方法区分了+(公共)/-(私有),但部分私有方法(如handleCurrentLevelRequests)逻辑复杂,可考虑拆分为独立类(如RequestHandler),进一步降低Main的复杂度;
    常量类的替代方案:StatusCode/DirectionCode用普通类定义常量,可优化为enum,更符合 Java 的枚举语义。
    第二题:
    代码质量度量图:
    image

解释:
1、基础信息
文件路径:D:\test\,项目名111,文件共 333 行代码、包含 185 条语句。
2、代码结构与复杂度
类与方法:包含 2 个类 / 接口,每个类平均 6.5 个方法,每个方法平均约 11.62 条语句。
复杂度:
最复杂的方法是Main.Main()(复杂度 1,位于第 47 行),整体平均复杂度仅 1.0,最大复杂度也只有 1(代码逻辑非常简单)。
分支与注释:
分支语句占比 24.9%(少量条件判断);
带注释的代码行仅占 3.3%(注释率极低,代码可读性差)。
3、方法复杂度详情
Main类的Main()方法数据(复杂度,语句数,最大深度,调用数):
Main.Main():1, 7, 2, 0(复杂度、嵌套深度都很低)
4、代码块嵌套深度分布
代码块嵌套深度对应的语句数:
深度 0(无嵌套):31 条
深度 1:62 条
深度 2:54 条
深度 3:30 条
深度 4:8 条
深度≥5:无语句(嵌套层次浅)
5、可视化图表
雷达图:展示了注释率、平均复杂度、方法数 / 类等指标(注释率维度明显偏低)。
柱状图:直观呈现不同嵌套深度的语句数量,主要集中在深度 1-2。
类图:
image

解释
这是电梯控制系统的 UML 类图,清晰呈现了系统的类结构、属性 / 方法、类间关系,核心内容如下:

  1. 类的结构与职责
    Main类(电梯核心控制类)
    属性:私有属性封装电梯状态(currentLevel当前楼层、currentDirection运行方向)、队列(interiorCalls内部呼叫队列、exteriorCalls外部呼叫队列)等核心数据;
    方法:
    公共方法(+):构造方法初始化电梯、registerCall注册呼叫、startOperation启动运行等对外接口;
    私有方法(-):navigateToDestination(导航到目标楼层)、requiresStop(判断是否停层)等内部业务逻辑,隐藏实现细节。
    CallRequest类(请求实体类)
    属性:封装呼叫信息(floorNumber目标楼层、moveDirection移动方向、isInsideCall是否内部呼叫);
    方法:重载构造方法,分别适配 “内部呼叫”(仅目标楼层)和 “外部呼叫”(呼叫楼层 + 方向)场景。
    StatusCode/DirectionCode类(常量类)
    用static final定义固定常量(如StatusCode.STATIONARY="STOPPED"表示静止状态,DirectionCode.ASCENDING=1表示上升方向),集中管理状态 / 方向的固定值。
  2. 类间关系
    依赖关系(虚线箭头):
    Main依赖StatusCode:Main的operationalStatus属性使用StatusCode的常量;
    Main/CallRequest依赖DirectionCode:两者的 “方向” 属性均使用DirectionCode的常量;
    聚合关系(隐含):Main的interiorCalls/exteriorCalls是Queue,说明Main“包含”CallRequest对象(类图中未显式标注,但属性可体现)。
    心得
  3. 设计的优点:符合 OOP 基础原则
    单一职责:类的职责边界清晰 ——Main管控制逻辑、CallRequest封装请求数据、StatusCode/DirectionCode管理常量,每个类只承担一个功能;
    封装性:核心数据(如currentLevel)设为私有,通过方法间接操作,既保护数据安全,又隐藏内部实现;
    常量集中化:将状态 / 方向的硬编码抽为独立类,避免代码中散列的魔法值,提升可维护性;
    构造方法重载:CallRequest用不同构造方法适配不同呼叫场景,体现了 “多态” 的基础设计思路。
  4. 设计的不足:可优化的方向
    依赖的扩展性问题:Main直接依赖StatusCode/DirectionCode的常量,若后期新增状态(如 “故障”)或方向,需修改Main代码,违背 “开闭原则”(对扩展开放、对修改关闭);
    关系的显式表达:Main与CallRequest的聚合关系未在类图中标注,团队协作时易忽略 “Main包含CallRequest” 的核心关联;
    方法的粒度问题:Main中私有方法(如handleCurrentLevelRequests)逻辑复杂,可拆分为独立类(如RequestHandler),进一步降低Main的复杂度。
    第三题:
    代码质量度量图
    image

解释:

  1. 基础信息
    文件路径:D:\test\,项目名9888,文件包含 341 行代码、180 条语句。
  2. 代码结构与复杂度
    包含 2 个类 / 接口,每个类平均 7 个方法,每个方法平均约 10.36 条语句。
    复杂度:最复杂的方法是Main.registerCall()(复杂度 3,位于第 57 行),整体平均复杂度 2.0,最大复杂度 3。
    分支与注释:分支语句占比 22.8%,带注释的代码行仅占 6.2%(注释率偏低)。
  3. 方法复杂度详情
    Main类的 2 个方法数据(复杂度,语句数,最大深度,调用数):
    Main():1, 7, 2, 0
    Main.registerCall():3, 4, 3, 2(是复杂度最高的方法)
  4. 代码块深度分布
    代码块嵌套深度对应的语句数:
    深度 0(无嵌套):31 条
    深度 1:63 条
    深度 2:59 条
    深度 3:27 条
    深度≥4:无语句(说明代码嵌套不深)
  5. 可视化图表
    雷达图:展示了注释率、平均复杂度、方法数 / 类等指标的分布。
    柱状图:直观呈现不同嵌套深度对应的语句数量,主要集中在深度 1-2。

类图:
image

解释
这是电梯控制系统的 UML 类图(在之前版本基础上补充了部分属性 / 方法),核心结构与关系如下:

  1. 类的结构与新增内容
    Main类(电梯核心控制类)
    新增属性:externalDestinations: Queue(存储外部请求的目的楼层),完善了外部请求的处理链路;
    新增方法:addExternalDestinationsToInterior()(将外部目的楼层转为内部请求),补充了外部请求的闭环逻辑;
    原有属性 / 方法:仍包含电梯状态(currentLevel等)、请求队列(interiorCalls/exteriorCalls)、核心业务方法(navigateToDestination/startOperation等)。
    CallRequest/StatusCode/DirectionCode类
    结构与之前一致:CallRequest封装请求信息,StatusCode定义运行状态常量,DirectionCode定义方向常量。
  2. 类间关系
    依赖关系:Main依赖StatusCode(状态常量),Main/CallRequest依赖DirectionCode(方向常量)(虚线箭头体现);
    聚合关系:Main的interiorCalls/exteriorCalls聚合CallRequest,新增的externalDestinations聚合Integer(目的楼层)。
    心得
  3. 设计的优化点:业务逻辑的闭环
    本次类图新增了externalDestinations属性和addExternalDestinationsToInterior()方法,体现了业务逻辑的完整性:
    外部请求的 “源楼层呼叫”+“目的楼层存储”+“转为内部请求” 形成闭环,解决了之前 “外部请求仅处理呼叫、未处理目的楼层” 的问题;
    方法的补充让Main类的职责更完整,符合 “电梯控制系统需处理内 / 外部全流程请求” 的业务场景。
  4. 设计的优点:职责更清晰
    业务链路完整:通过新增属性 / 方法,外部请求的 “注册→存储目的楼层→转为内部请求” 流程在类图中清晰体现;
    封装性增强:externalDestinations设为私有,仅通过addExternalDestinationsToInterior()操作,保证数据安全;
    方法粒度合理:新增方法addExternalDestinationsToInterior()专注于 “外部目的楼层转内部请求”,符合单一职责原则。
  5. 仍可优化的方向
    依赖的解耦:Main直接依赖StatusCode/DirectionCode的常量,若后续扩展状态 / 方向,需修改Main代码;可将常量改为枚举,并通过接口依赖(而非直接依赖类)降低耦合;
    聚合关系的显式标注:Main与externalDestinations(Queue)的聚合关系未在类图标注,团队协作时易忽略该数据链路;
    复杂方法的拆分:Main中handleCurrentLevelRequests()等方法逻辑仍较复杂,可抽离为独立的RequestHandler类,进一步降低Main的复杂度。
  6. 总结:类图迭代反映系统设计的演进
    本次类图的迭代是 “业务驱动设计完善” 的典型体现:从 “仅处理请求注册” 到 “覆盖外部请求全链路”,类图的变化对应了业务逻辑的补全。核心启发是:
    类图是系统设计的 “快照”,需随业务逻辑的完善同步更新;
    好的设计是 “迭代式” 的:先满足核心功能,再逐步补充细节、优化结构;
    类图的 “完整性” 直接影响代码的可维护性 —— 清晰的属性 / 方法 / 关系,能降低团队协作的沟通成本。
    三、踩坑心得
    第一题:
    测试用例:
    1
    20
    ❤️,UP>
    <5>
    <6,DOWN>
    <7>
    <3>
    End
    我的输出:
    Current Floor: 1 Direction: UP
    Current Floor: 2 Direction: UP
    Current Floor: 3 Direction: UP
    Open Door # Floor 3
    Close Door
    Current Floor: 4 Direction: UP
    Current Floor: 5 Direction: UP
    Open Door # Floor 5
    Close Door
    Current Floor: 6 Direction: UP
    Current Floor: 7 Direction: UP
    Open Door # Floor 7
    Close Door
    Current Floor: 6 Direction: DOWN
    Open Door # Floor 6
    Close Door
    Current Floor: 5 Direction: DOWN
    Current Floor: 4 Direction: DOWN
    Current Floor: 3 Direction: DOWN
    Open Door # Floor 3
    Close Door

正确输出:
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door
踩坑原因:

  1. 外部下行请求的判断逻辑有漏洞
    测试用例里的<6,DOWN>,你的代码里addExternalRequest方法会把 6 加入externalDownRequests,但在hasRequestsInDirection(Direction.DOWN)里,判断 “下方是否有下行请求” 时,是从currentFloor-1往下找 —— 这次刚好 6 在电梯上行到 7 之后往下走时能命中,但如果遇到:
    电梯在 3 楼(UP 方向),此时有个<10,DOWN>的请求(10 楼想往下)
    你的代码会认为 “UP 方向有请求(10 在 3 上方)”,先往上走,但 10 楼的 DOWN 请求其实是 “想让电梯到 10 楼后往下”,逻辑本身没问题,但如果是<2,DOWN>(2 楼在 3 下方),你的代码会先处理完 UP 方向所有请求,再回头处理 DOWN,这个逻辑是对的,但如果想让 “同方向优先” 更精准,需要注意。
  2. 初始化方向固定为 UP 不符合实际场景
    你在Elevator构造方法里把currentDirection默认设为UP,如果测试用例是:
    最小楼层 1,最大楼层 20
    第一个请求是<5,DOWN>
    你的代码会先从 1 楼 UP 到 5 楼(哪怕 5 楼是 DOWN 请求),再往下走 —— 虽然逻辑上能处理,但实际电梯初始化应该是NONE(无方向),先判断所有请求再定方向,你现在的写法会让 “初始无请求时,电梯先默认往上走一层”(但这次测试用例里因为有请求,没暴露这个问题)。
  3. Set 集合存储请求丢重复请求
    你用HashSet存internalRequests/externalUpRequests等,比如如果输入两次<3>,HashSet 只会存一个 3—— 但实际电梯里,哪怕按两次 3 楼,也应该响应一次,但如果是 “多人同时按 3 楼”,你的代码不会记录多次,但电梯本身只需要停一次,所以这个点不算 “坑”,只是和实际场景的 “请求计数” 逻辑有差异。

第二题:
测试用例:
1
20
❤️,UP>
❤️,UP>
<5>
<5>
<5>
<6,DOWN>
<7>
<7>
<3>
<22,DOWN>
<5,DOWN>
<30>
END
我的输出:
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Current Floor: 8 Direction: UP
Current Floor: 9 Direction: UP
Current Floor: 10 Direction: UP
Current Floor: 11 Direction: UP
Current Floor: 12 Direction: UP
Current Floor: 13 Direction: UP
Current Floor: 14 Direction: UP
Current Floor: 15 Direction: UP
Current Floor: 16 Direction: UP
Current Floor: 17 Direction: UP
Current Floor: 18 Direction: UP
Current Floor: 19 Direction: UP
Current Floor: 20 Direction: UP
Current Floor: 21 Direction: UP
Current Floor: 22 Direction: UP
Current Floor: 23 Direction: UP
Current Floor: 24 Direction: UP
Current Floor: 25 Direction: UP
Current Floor: 26 Direction: UP
Current Floor: 27 Direction: UP
Current Floor: 28 Direction: UP
Current Floor: 29 Direction: UP
Current Floor: 30 Direction: UP
Open Door # Floor 30
Close Door
Current Floor: 29 Direction: DOWN
Current Floor: 28 Direction: DOWN
Current Floor: 27 Direction: DOWN
Current Floor: 26 Direction: DOWN
Current Floor: 25 Direction: DOWN
Current Floor: 24 Direction: DOWN
Current Floor: 23 Direction: DOWN
Current Floor: 22 Direction: DOWN
Open Door # Floor 22
Close Door
Current Floor: 21 Direction: DOWN
Current Floor: 20 Direction: DOWN
Current Floor: 19 Direction: DOWN
Current Floor: 18 Direction: DOWN
Current Floor: 17 Direction: DOWN
Current Floor: 16 Direction: DOWN
Current Floor: 15 Direction: DOWN
Current Floor: 14 Direction: DOWN
Current Floor: 13 Direction: DOWN
Current Floor: 12 Direction: DOWN
Current Floor: 11 Direction: DOWN
Current Floor: 10 Direction: DOWN
Current Floor: 9 Direction: DOWN
Current Floor: 8 Direction: DOWN
Current Floor: 7 Direction: DOWN
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Open Door # Floor 5
Close Door
正确输出:
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Open Door # Floor 5
Close Door
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door
踩坑原因:
1:判断 “下一个目标” 的逻辑有漏洞,找不到目标还硬跑
测试用例里的请求处理完(3、5、7、6、3 楼都处理完了),理论上电梯该停,但你的determineNextTarget()方法在 “内外请求队列都空了” 的情况下才返回 null,可实际代码里:
用的是Queue(队列),handleCurrentLevelRequests()里虽然移除了 “当前楼层” 的请求,但队列里可能还有 “没处理干净的无效请求”
更关键的是:findAscendingTarget()/findDescendingTarget()在 “没请求” 时返回 null,但determineNextTarget()里,当currentDirection不是 IDLE 时,会直接调用这两个方法 —— 如果返回 null,targetLevel就成了 null ,代码里没做 “返回 null 就终止” 。
比如处理完所有请求后,findAscendingTarget()返回 null,determineNextTarget()也返回 null,startOperation()里的循环本应 break,但代码里navigateToDestination(targetLevel)传入 null,直接导致电梯从当前楼层(比如 3 楼)开始无限制往上走,直到超出 20 楼的范围,还在继续跑。
2:“是否需要停层” 的判断逻辑重复且漏判
正确输出里,电梯下行到 5 楼时会停(Open Door # Floor 5),但我的输出里没停,原因是:
requiresStop()方法里的判断条件太冗余,还互相冲突:
你先判断 “外部请求队头是否是当前方向 + 当前楼层”,再判断 “内部请求队头是否是当前楼层”,但处理完 3、5、7 楼后,5 楼的内部请求其实还在队列里,却因为handleCurrentLevelRequests()里重复移除请求,导致 “5 楼的请求被提前删掉了”;
比如电梯下行到 5 楼时,interiorCalls里的 5 楼请求已经被误删,所以requiresStop()返回 false,电梯不停,直接往下走。
核心坑 3:方向切换逻辑 “一根筋”,没请求了还硬切换方向
startOperation()里的这段代码是大问题:
if (currentDirection == DirectionCode.ASCENDING && findAscendingTarget() == null) {
currentDirection = DirectionCode.DESCENDING;
} else if (currentDirection == DirectionCode.DESCENDING && findDescendingTarget() == null) {
currentDirection = DirectionCode.ASCENDING;
}
电梯只要当前是 UP 方向,就强制切到 DOWN;是 DOWN 就切到 UP。
第三题:
测试用例:
1
20
<5,9>
<8>
<9,3>
<4>
<2>
End
我的输出:
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Current Floor: 8 Direction: UP
Open Door # Floor 8
Close Door
Current Floor: 7 Direction: DOWN
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Open Door # Floor 4
Close Door
Current Floor: 3 Direction: DOWN
Current Floor: 2 Direction: DOWN
Open Door # Floor 2
Close Door
Current Floor: 3 Direction: UP
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Current Floor: 8 Direction: UP
Current Floor: 9 Direction: UP
Open Door # Floor 9
Close Door
Current Floor: 8 Direction: DOWN
Current Floor: 7 Direction: DOWN
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Current Floor: 8 Direction: UP
Current Floor: 9 Direction: UP
Open Door # Floor 9
Close Door
正确输出:
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Current Floor: 8 Direction: UP
Open Door # Floor 8
Close Door
Current Floor: 9 Direction: UP
Open Door # Floor 9
Close Door
Current Floor: 8 Direction: DOWN
Current Floor: 7 Direction: DOWN
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Open Door # Floor 4
Close Door
Current Floor: 3 Direction: DOWN
Current Floor: 2 Direction: DOWN
Open Door # Floor 2
Close Door
Current Floor: 3 Direction: UP
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Current Floor: 8 Direction: UP
Current Floor: 9 Direction: UP
Open Door # Floor 9
Close Door
Current Floor: 8 Direction: DOWN
Current Floor: 7 Direction: DOWN
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door
踩坑原因:
1、在private Integer findAscendingTarget()中,遍历两个队列了,正确答案应该是只比较两个队列的队头
2、找下一个目标的逻辑有问题(findAscendingTarget/findDescendingTarget)
改后的findAscendingTarget只看外部、内部队列的队头,但没考虑 “电梯往上走时,只要前方(更高楼层)有请求,就该先走完上行的所有请求,再往下”。比如电梯到 8 楼时:
内部队列里其实有 9 楼的请求(<5,9> 的目的楼层),但代码只对比 “外部队头(此时是 < 9,3 > 的 9 楼下行请求)” 和 “内部队头(可能是 4 楼?)”,误以为 8 楼之后最近的上行目标没有了,就直接掉头往下走,没先去 9 楼。
正确逻辑是:电梯往上走时,只要上行方向还有未处理的请求,就该先往上到最高的那个请求楼层,再往下。只看队头,直接断了上行的路。
2. requiresStop(判断是否该停)的逻辑太绕,还多判了没必要的条件
写的requiresStop里加了很多冗余判断,比如:
if (interiorCalls.isEmpty() && !exteriorCalls.isEmpty() && exteriorCalls.peek().floorNumber == currentLevel) {
return true;
}
这些多余的判断导致电梯到 8 楼后,程序误判 “没有更高的上行请求了”,直接切换成下行。
3. handleCurrentLevelRequests(处理当前楼层请求)重复处理,把队列搞乱了
在这个方法里写了好几遍 “循环 poll 队头”,
while (!interiorCalls.isEmpty() && interiorCalls.peek().floorNumber == currentLevel) {
interiorCalls.poll();
}
// 后面又写一遍几乎一样的
while (!interiorCalls.isEmpty() && interiorCalls.peek().floorNumber == currentLevel) {
interiorCalls.poll();
}
重复 poll 会导致内部队列里的 9 楼请求被提前删掉(或者位置乱了),等电梯到 8 楼想找 9 楼请求时,队头已经不是 9 楼了。
4. 电梯方向切换太快
在startOperation里,写了:

if (currentDirection == DirectionCode.ASCENDING && findAscendingTarget() == null) {
currentDirection = DirectionCode.DESCENDING;
}
因为findAscendingTarget只看队头,误判 “没有上行目标了”,所以电梯到 8 楼就立刻切下行,没先去 9 楼

四、改进建议

  1. 找下一个目标的逻辑有问题(findAscendingTarget/findDescendingTarget)
    改后的findAscendingTarget只看外部、内部队列的队头,但没考虑 “电梯往上走时,只要前方(更高楼层)有请求,就该先走完上行的所有请求,再往下”。比如电梯到 8 楼时:
    内部队列里其实有 9 楼的请求(<5,9> 的目的楼层),但代码只对比 “外部队头(此时是 < 9,3 > 的 9 楼下行请求)” 和 “内部队头(可能是 4 楼?)”,误以为 8 楼之后最近的上行目标没有了,就直接掉头往下走,没先去 9 楼。
    正确逻辑是:电梯往上走时,只要上行方向还有未处理的请求,就该先往上到最高的那个请求楼层,再往下。只看队头,直接断了上行的路。
  2. requiresStop(判断是否该停)的逻辑太绕,还多判了没必要的条件
    写的requiresStop里加了很多冗余判断,比如:

if (interiorCalls.isEmpty() && !exteriorCalls.isEmpty() && exteriorCalls.peek().floorNumber == currentLevel) {
return true;
}
这些多余的判断导致电梯到 8 楼后,程序误判 “没有更高的上行请求了”,直接切换成下行。其实判断是否该停,核心就 2 点:
外部队列队头是不是当前楼层 + 同方向
内部队列队头是不是当前楼层
多出来的判断反而干扰了逻辑,让电梯提前掉头。
3. handleCurrentLevelRequests(处理当前楼层请求)重复处理,把队列搞乱了
你在这个方法里写了好几遍 “循环 poll 队头”
while (!interiorCalls.isEmpty() && interiorCalls.peek().floorNumber == currentLevel) {
interiorCalls.poll();
}
// 后面又写一遍几乎一样的
while (!interiorCalls.isEmpty() && interiorCalls.peek().floorNumber == currentLevel) {
interiorCalls.poll();
}
重复 poll 会导致内部队列里的 9 楼请求被提前删掉(或者位置乱了),等电梯到 8 楼想找 9 楼请求时,队头已经不是 9 楼了,自然就往下去了。
4. 电梯方向切换太快
在startOperation里,写了:

if (currentDirection == DirectionCode.ASCENDING && findAscendingTarget() == null) {
currentDirection = DirectionCode.DESCENDING;
}
因为findAscendingTarget只看队头,误判 “没有上行目标了”,所以电梯到 8 楼就立刻切下行,没先去 9 楼(正确逻辑是 9 楼还有上行请求,该先去 9 楼再下行)。
五、总结

核心编程能力层面
1、搞懂了面向对象的基础应用:比如这次电梯模拟里,能把电梯的请求(CallRequest)、方向(DirectionCode)拆成独立的类,不再把所有逻辑堆在一个 main 方法里,知道了 “类是用来封装属性和行为” 的实际意义,不是光记概念。
2、掌握了队列(Queue)的实际用法:之前只知道队列是 “先进先出”,这次用 LinkedList 实现电梯的内外请求队列,真的理解了 peek(看队头)、poll(取队头)、isEmpty(判空)这些方法在实际场景里怎么用,比如判断电梯该不该停、该处理哪个请求。
3、理解了业务逻辑和代码的结合:电梯模拟不是单纯写语法,而是要贴合现实中电梯的运行规则 —— 比如 “同方向先跑完所有请求再掉头”“到楼层要开门 / 关门”“内外请求要区分处理”,明白了写代码不是堆功能,而是要先把业务规则理清楚。

问题解决层面
学会了 “对比输出找问题”:这次自己的输出和正确输出不一样,能一步步定位到是 “找目标楼层的逻辑错了”“方向切换太急躁”,而不是瞎改代码,知道了先定位问题根源再动手。
意识到 “简单逻辑比复杂判断更靠谱”:一开始写 requiresStop、handleCurrentLevelRequests 时加了一堆冗余条件,结果越改越错,后来发现核心逻辑就 2-3 条,删了多余的反而对了,明白代码不是写得越多越好,简洁才不容易出 bug。

需要进一步学习和研究的地方
1、复杂业务逻辑的梳理能力:电梯模拟只是简单的业务场景,这次还是容易把 “只比较队头” 和 “同方向跑完所有请求” 的规则搞混,后续要练 “把现实规则转化为清晰代码逻辑” 的能力,比如先画流程图(比如电梯找目标→运行→停→处理请求→切换方向的流程),再写代码,而不是边想边写。
2、数据结构的灵活应用:这次只用了队列,但实际电梯场景可能需要更合适的结构(比如优先级队列,按楼层距离排序请求),后续要研究不同数据结构在业务场景里的适配性,比如什么时候用队列、什么时候用优先级队列、什么时候用集合。
3、边界条件的考虑:比如电梯到顶层 / 底层该怎么处理、空队列时的判空逻辑、重复请求的处理(比如多次按同一楼层),这次虽然没踩这些坑,但实际开发中边界条件很容易出问题,需要刻意练习 “把所有可能的情况都想到”。
4、代码调试和测试能力:这次是靠对比输出找问题,效率不高,后续要学怎么用调试工具(比如 IDEA 的断点)一步步看变量变化(比如 currentLevel、currentDirection、队列内容的变化),快速定位哪一步逻辑错了,而不是靠 “猜”。

对课程、作业等方面的改进建议

  1. 对教师和课上的建议
    多结合 “生活化场景” 讲知识点:比如讲队列时,除了说 “先进先出”,可以直接拿电梯请求队列举例,比单纯讲理论好懂;讲逻辑判断时,就用电梯 “该不该停”“该往哪走” 这种场景,抽象的语法和逻辑结合具体场景,记起来更牢。
    课上多留 “找错改错题”:比如给一段有问题的电梯代码(像我这次写错的 findAscendingTarget),让大家课上找问题、改逻辑,比单纯让大家从头写代码更能练出 “找 bug” 的能力,也更贴近实际开发。
  2. 对作业建议
    多给测试点:比如电梯作业,先让提交 “请求注册 + 队列存储” 的部分,老师批改完,再提交 “找目标楼层 + 运行” 的部分,最后提交 “处理请求 + 方向切换”,避免一次性写完整段代码,出错了不知道哪一步的问题。
posted @ 2025-11-22 20:52  Blueeeeeeeeeee  阅读(0)  评论(0)    收藏  举报