一、前言
1. 核心知识点
三次pta大作业围绕电梯调度展开,核心知识点逐渐深入:
- 基础语法:从简单的输入输出、条件判断、循环,到枚举(enum)、类与对象的使用。
- 数据结构:用到了链表(LinkedList)存储请求队列,哈希集合(HashSet)实现请求去重。
- 面向对象:从单类实现,到拆分出请求类(PassengerRequest)、队列类(RequestQueue)、控制器类(ElevatorController),逐步体现封装和分工思想。
- 逻辑调度:核心是电梯的请求处理逻辑,包括判断方向一致性、选择最近目标楼层等简单调度算法。
2. 题量与迭代
三次题目集是递进式迭代,题量相似但功能逐步完善:
- 第一次:实现最基础的电梯功能,区分内部 / 外部请求,处理简单的上下行逻辑。
- 第二次:封装请求为PassengerRequest类,拆分出请求队列和控制器,逻辑更清晰。
- 第三次:优化细节,比如更明确的错误提示、完善队列操作方法,让调度逻辑更严谨。
3. 难度变化
- 初期(第一次):主要难点是理解内部 / 外部请求的区别,以及电梯移动的基础逻辑(循环打印楼层)。
- 中期(第二次):需要理解类的拆分思想,比如为什么要把请求、队列、控制器分开写,刚开始可能觉得 “多此一举”,但慢慢能体会到代码复用的好处。
- 后期(第三次):难点在调度逻辑的细节优化,比如如何判断 “同方向优先”“距离更近优先”,需要理清多个条件的优先级。
总体来说,三次迭代更像是 “一步一个脚印” 的学习过程,从 “能跑起来” 到 “跑起来更合理”。
二、设计与分析
1. 第一次大作业:先让电梯 “动起来”
(1)第一次就俩类:Elevator(管电梯所有事)和Main(管输入):

- Elevator里塞了 “当前楼层”“请求队列”“移动方法”,啥都管;
- 用ElevatorState枚举标电梯状态(停 / 跑 / 门开门关),比写 “true/false” 清楚。
(2)代码质量
总览:

详细报表:

分析:
- 代码 220 行,基本没写注释;
- 一个类里没拆分方法,最大方法写了 33 行,看半天才能懂;
- 没分 “内部 / 外部请求”,逻辑堆一起,改起来费劲。
(3)结论
第一次最大的收获是 “电梯能跑了”,但工具报表提醒我:代码堆一起 = 难维护。比如想改 “请求怎么存”,得把整个Elevator类翻一遍。
2. 第二次大作业:把功能 “拆分开”
(1)这次拆成了 5 个类,各有各的功能:

- 新增PassengerRequest:专门存 “谁按的、按的哪层”;
- 新增RequestQueue:专门管请求的 “存 / 取 / 去重”;
- 新增ElevatorController:专门管 “先处理哪个请求”;
- Elevator只负责 “移动”“开关门”。
(2)代码质量
总览:

详细报表:

分析
- 代码 300 行,关键逻辑标了注释;
- 每个类平均 10 个方法,方法变短了(平均 6.6 行),一眼能看懂;
- 但PassengerRequest的equals()方法有点复杂,工具标了 “最大复杂度 6”。
(3)结论
拆分之后,代码清爽多了!比如要改 “怎么选下一个请求”,只动ElevatorController就行。工具报表也告诉我:拆分 = 代码变简单。
3. 第三次大作业:把细节 “磨细点”
(1)代码类图:

- RequestQueue新增了 “判断内部请求空不空”“外部请求空不空” 的方法;
- 每个类的方法更短,逻辑更专一。
(2)代码质量
总览:

详细报表:

分析:
- 代码 346 行,注释基本关键地方都写了;
- 最大复杂度降到 1,方法平均 7 行,看一眼就知道干啥;
- 类和方法的分工更明确,工具标了 “平均复杂度 1.00”。
(3)结论
第三次最大的收获是 “细节决定体验”,比如RequestQueue加了 “判断队列空不空” 的方法,调度逻辑不用自己数队列长度了,细节磨得越细,代码越好写。
三、采坑心得
<1>第一次大作业踩的坑:“单类堆所有功能”,代码又点混乱。
输入样例
1
20
<3,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
①第一次写代码时,把 “电梯移动、请求管理、调度逻辑” 全塞在Elevator类里:
private int currentFloor = 1;
private LinkedList<Integer> internalQueue; // 内部请求
private LinkedList<Integer> externalUpQueue; // 外部上行请求// 一个方法干所有事:调度+移动+开门
public void processAll() {// 1. 判断方向(写了5行if-else)// 2. 选目标楼层(又写了5行if-else)// 3. 移动(循环打印楼层)// 4. 开门关门(打印提示)
}
心得:当测试输入<3,UP>、<5>时,改了 processAll里 的代码,结果电梯到 3 楼没停,直接跑 5 楼了 —— 输出里少了 “Open Door # Floor 3”,找错要找半天才改的过来,不分类,在修改自己代码错误时会十分难受。
<2>第二次大作业:请求去重自己写循环
过滤重复请求时,没用HashSet,写了循环:
class RequestQueue {public void addInternal(int floor) {// 循环查队列,有没有重复请求for (int f : internalQueue) {if (f == floor) return;}internalQueue.add(floor);}
}
在测试输入 5 个<3>时,程序卡了, 但改后用HashSet,输入样例里
的<3,UP><3,UP>自动过滤成 1 个,输出只开了 1 次 3 楼的门。
改用哈希后就没有卡顿:
private Set<Integer> internalSet = new HashSet<>();
public void addInternal(int floor) {if (internalSet.add(floor)) { internalQueue.add(floor);}
}
心得:我觉得合理用一些Java 库里现成的工具比我们自己手写一个更加方便和高效,不会出现太多的bug。
<3>第三次大作业:类拆分了,逻辑还存在些漏洞
就比如说需求让 “外部请求处理后,把目的楼层加内部队列”,我写在Elevator的openDoor里:
class Elevator {public void openDoor() {System.out.println("开门");requestQueue.addInternal(passenger.getTargetFloor());}
}
这导致了测试输入<5,4>,电梯到 5 楼开门后,不会把 4 楼加入内部队列,输出里没有 “Current Floor: 4 Direction: DOWN”,乘客没去成目的地。
修改:电梯类不应该管队列的,应该把队列放在已有的ElevatorController类里:
class ElevatorController {public void processPassenger(Passenger p) {if (p是外部请求) { // 比如p的sourceFloor≠当前楼层requestQueue.addInternal(p.getTargetFloor()); // 转内部队列}}
}
心得:各个类要分清楚自己负责的部分,如果混着用,后续改代码容易出连锁 bug,导致一堆错误。
四、改进建议
- 类再拆细一点:把现在的RequestQueue分成两个类 ——InternalQueue(专门管电梯里的请求)和ExternalQueue(专门管外面的请求),这样调度时不用来回判断 “是内部还是外部”,直接调用对应队列的方法,不容易乱。
- 重复代码抽出来:三次作业里,“判断楼层是不是有效(没超最大 / 最小楼层)” 写了好几次,下次可以单独写个Tool类,把 “校验楼层”“解析输入” 这些重复的功能放进去,后续直接调用,不用重复写代码。
- 测试用例记下来:把之前的输入样例(比如<3,UP>、<5,4>)整理成一个 “测试清单”,每次改代码后,按清单跑一遍,确保之前的功能没被改坏 。
五、总结
- 这三次作业学到的知识
类别堆一起:一开始把所有功能塞一个类里,改一点就崩;后来学会按 “干活分工” 拆类(电梯管移动、调度管安排、队列管存请求),代码好懂多了。 - 会用现成工具:之前自己写循环去重,又慢又错;现在知道用HashSet一句话去重,省了好多事 ,除了HashSet,再学学 Java 里其他好用的类,更加高效的帮助自己写代码。
- 逻辑要清晰:明白外部请求的流程是 “外面按电梯→电梯到源楼层接人→把目的楼层加到内部请求→送乘客到目的地”,每个步骤要交给对应类干,不能乱。