一、前言
对三次题目总结:
三次题目难度逐渐递增。题目数量适当,规定时间内足已完成。每一次题目集前面小题像是练手最后一题难度飙升是真正考验学习成果的时候。第一次题目集让人眼前一亮以前从没做过类似的题目,光是搞懂题目的逻辑就不是一件简单的事,而且题目要求和现实电梯运行情况有所不同。将题目一琢磨透了之后后面的题目就变得相对轻松了,第二次题目集主要考察对类的运用,在第一次题目的基础上将电梯类分装成几个类,遵循单一职责原则,更有利于代码的迭代。第三次题目又在第二次题目的基础上进行迭代,类的设计稍有改变,电梯运行规则有所变化,对电梯请求的处理有所改变。三次题目下来感觉到自己的代码编写能力有显著提高。
二、设计与分析
第一次电梯题目分析
题目要求:编写一个单部电梯调度程序。设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。根据请求队列以及电梯调度算法来确定电梯的运行逻辑,请求的合理移除。最后打印出电梯每次运行的楼层和状态。
输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:
运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
输入样例:
在这里给出一组输入。例如:
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
类图设计:

Main类:使用正则表达式来正确读取每行输入当中所需的数据,然后按数据的形式来进行外部或者内部的请求的加入。
ExternalRequest类:更好的存储和拿取外部请求的楼层与方向。
Elevator类:该类与外部请求类相关联,主要用来控制电梯的运行,和方向的确定,以及打印电梯当前所在的楼层、状态和运行方向。
Source Monitor分析结果:

分析与心得
由上面的Source Monitor分析的结果来看不难发现,这次题目集提交的通过代码的存在的主要问题有两个
1.代码复杂度过高:代码复杂度高达29严重超标,正常情况下应该不超过10。最大嵌套深度6层逻辑层次过深,通常应不超过3层。究其原因可能在于单一类承担过多职责,第一次题目未能遵循单一职责原则,使得代码质量以及可迭代性都较为低下。
2.代码注释严重不足仅有3%的注释行,远低于推荐的20-30%这样使得代码的可读性大大降低,写代码时未考虑后续的迭代
第二次电梯题目的设计与分析
题目要求:对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客请求类、队列类以及控制类,参考类图如下:

Main类
设计定位:程序入口和输入处理层
核心功能:
与上一次题目集电梯题目的Main类设计相似
使用正则表达式解析每行用户输入的数据
根据输入数据类型区分电梯内部请求和外部请求
将解析后的请求分别加入到RequestQueue类的对应请求队列中
负责程序的启动和初始参数配置
Elevator类
设计定位:电梯实体模型层
核心功能:
包含电梯可达的楼层范围(minFloor, maxFloor)
管理电梯当前所在楼层(currentFloor)
提供楼层合法性检验方法(isValidFloor)
维护电梯的运行方向(direction)和运行状态(state)
为Controller类提供电梯运行逻辑分析所需的基础状态信息
通过getter/setter方法封装状态访问
ExternalRequest类
设计定位:外部请求数据模型
核心功能:
专门存储电梯外部请求的楼层(floor)与方向(direction)
用于RequestQueue类中添加外部请求的数据封装
使添加或使用外部电梯请求的逻辑更清晰、类型安全
通过构造函数确保外部请求数据的完整性
RequestQueue类
设计定位:请求管理调度层
核心功能:
维护电梯内部请求队列(internalRequests)和外部请求队列(externalRequests)
提供添加内部请求(addInternalRequest)和外部请求(addExternalRequest)的方法
实现获得当前请求和检验请求是否重复的功能
为Controller类提供电梯运行方向分析和判断所需的请求数据
负责请求队列的维护和状态管理
Controller类
设计定位:业务逻辑控制层
核心功能:
关联Elevator类和RequestQueue类,协调两者之间的交互
执行电梯运行方向及状态判断(determineDirection)
控制电梯的移动过程(move)
完成请求队列中的请求处理(processRequests)
判断电梯是否应该在当前楼层停止(shouldStop)
计算下一目标楼层(getNextFloor)
打印当前电梯的运行状态和楼层信息
实现电梯运行的核心调度算法
枚举类型
设计定位:类型安全定义层
核心功能:
Direction枚举:描述电梯的三种运行方向(UP、DOWN、IDLE)
State枚举:描述电梯的三种不同运行状态(MOVING、STOPPED)
替代字符串常量,使代码更加简洁易懂
提供编译时类型检查,避免运行时错误
方便后续的逻辑运行和状态判断
电梯运行规则与前阶段单类设计相同,但要处理如下情况:
乘客请求楼层数有误,具体为高于最高楼层数或低于最低楼层数,处理方法:程序自动忽略此类输入,继续执行
乘客请求不合理,具体为输入时出现连续的相同请求,例如<3><3><3>或者<5,DOWN><5,DOWN>,处理方法:程序自动忽略相同的多余输入,继续执行,例如<3><3><3>过滤为<3>
输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:
运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
输入样例1:
在这里给出一组输入。例如:
1
20
< 3,UP>
<5>
<6,DOWN>
<7>
<3>
end
输出样例1:
在这里给出相应的输出。例如:
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
输入样例2:
在这里给出一组输入。例如:
1
20
< 3,UP>
< 3,UP>
<5>
<5>
<5>
<6,DOWN>
<7>
<7>
<3>
<22,DOWN>
<5,DOWN>
<30>
END
输出样例2:
在这里给出相应的输出。例如:
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
Source Monitor分析结果:

分析与心得:
由上面的Source Monitor分析的结果来看,第二次提交的通过代码在有了题目给的参考类图的设计后,职责拆分后新增 Controller 类和RequestQueue 类,符合单一职责原则。质量明显要比第一次的好一些,但是还是存在一些问题的:
1.注释覆盖率仍然偏低:可能原因是第二次题目代码量增加,在第一次的基础上增加了一些注释量,但总体代码注释量还是偏低。
2.分支语句比例偏高:117%的分支语句比例表明条件逻辑仍然较多可能隐藏复杂业务规则
此次代码经过迭代后满足了单一职责原则相较于第一次电梯题目有了很大的改善,但还没有养成良好的写注释的习惯
第三次电梯题目的设计与分析
题目要求:对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客类、队列类以及控制类,具体设计可参考如下类图。

各个类的设计及功能分析
Main类
设计定位:程序入口和输入处理层
核心功能:
与上一次题目集电梯题目的Main类设计相似
使用正则表达式解析每行用户输入的数据
根据输入数据类型区分电梯内部请求和外部请求
将解析后的请求分别加入到RequestQueue类的对应请求队列中
负责程序的启动和初始参数配置
Elevator类
设计定位:电梯实体模型层
核心功能:
包含电梯可达的楼层范围(minFloor, maxFloor)
管理电梯当前所在楼层(currentFloor)
提供楼层合法性检验方法(isValidFloor)
维护电梯的运行方向(direction)和运行状态(state)
为Controller类提供电梯运行逻辑分析所需的基础状态信息
通过getter/setter方法封装状态访问
Passenger类
设计定位:乘客请求数据模型
核心功能:
存储乘客的源楼层(sourceFloor)和目标楼层(destinationFloor)
替代原有的ExternalRequest类,统一内部和外部请求的数据结构
通过不同构造函数支持外部请求(含源楼层)和内部请求(仅目标楼层)
提供计算请求方向的方法(getDirection)
使请求管理更加面向对象,逻辑更清晰
RequestQueue类
设计定位:请求管理调度层
核心功能:
维护电梯内部请求队列(internalRequests)和外部请求队列(externalRequests),均使用Passenger对象
提供添加内部请求(addInternalRequest)和外部请求(addExternalRequest)的方法
实现获得当前请求和队列状态检查的方法
为Controller类提供电梯运行方向分析和判断所需的请求数据
负责请求队列的维护和状态管理
Controller类
设计定位:业务逻辑控制层
核心功能:
关联Elevator类和RequestQueue类,协调两者之间的交互
执行电梯运行方向及状态判断(determineDirection)
控制电梯的移动过程(move)
完成请求队列中的请求处理(processRequests)
判断电梯是否应该在当前楼层停止(shouldStop)
计算下一目标楼层(getNextFloor)和最近楼层(getClosest)
实现开门(openDoor)和清理已服务请求(removeRequests)操作
打印当前电梯的运行状态和楼层信息
枚举类型
设计定位:类型安全定义层
核心功能:
Direction枚举:描述电梯的三种运行方向(UP、DOWN、IDLE)
State枚举:描述电梯的两种运行状态(MOVING、STOPPED)
替代字符串常量,使代码更加简洁易懂
提供编译时类型检查,避免运行时错误
方便后续的逻辑运行和状态判断
电梯运行规则与前阶段相同,但有如下变动情况:
乘客请求输入变动情况:外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)
输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<请求源楼层,请求目的楼层>,其中,请求源楼层表示乘客发起请求所在的楼层,请求目的楼层表示乘客想要到达的楼层。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:
运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
输入样例1:
在这里给出一组输入。例如:
1
20
<5,4>
<5>
<7>
end
输出样例1:
在这里给出相应的输出。例如:
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
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Open Door # Floor 5
Close Door
Current Floor: 4 Direction: DOWN
Open Door # Floor 4
Close Door
输入样例2:
在这里给出一组输入。例如:
1
20
<5,9>
<8>
<9,3>
<4>
<2>
end
输出样例2:
在这里给出相应的输出。例如:
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
Source Monitor分析结果:

分析与心得:此次作业代码量有显著提高,也算是写代码时的一个小心思吧,为了后续更好的看懂代码不用再一点一点的从头开始看。平均方法语句数维持在一个较好的水平,但是最大嵌套深度稍有回升,可能是处理某个特殊请求时进行了多层嵌套。分支语句比例较上次少有降低,但总体还是偏高。
三、踩坑心得
1、电梯调度算法理解有误
程序运行后缺少后续 6→5、5→4、4→3 的移动和停靠输出。这是因为程序在处理内部请求时将内部请求按楼层从小到大给自动排序这样就会导致在后面输入的较小的楼层数请求在前面停靠并输出结果,但这却是日常生活中的电梯正常运行的方式,由于惯性思维我们在解答此题时容易按照实际情况来编写代码。而题目要求是建立外部请求队列和内部请求队列处理请求时比较两队列队首遵循同向优先其次近距离的规则来进行电梯调度。
2、答案错误和代码运行超时
第二次电梯题目实际上我是没有做出来的,两个案例都可以通过但是提交代码后显示答案错误和运行超时,问过同学才知道测试案例只是提供一个检测的方法并不是真正的测试点
答案错误可能是测试点包含了多种情况如正常情况和特殊情况都在这个测试点里面并没有完全通过。运行超时问题我通过查阅资料得知可能是嵌套层数过多或者参数设置过多会增加代码运行时间导致超时
3、未知错误
第三次电梯题目测试点一,一开始我以为是没有加入去重操作,后来回去阅读代码发现去重操作已经添加了,可是仍然过不了第一个测试点,我猜测可能是某个特殊的输入测试无法通过
可是我和室友讨论过后把一些可能想到的测试点都试了一遍,有发现错误的也进行了修改还是无法通过,也就不了了之了。
四、改进建议
- 引入调度策略模式
点击查看代码
// 替换当前的硬编码调度逻辑
interface SchedulingStrategy {Integer getNextFloor(int currentFloor, Direction direction, List<Passenger> internal, List<Passenger> external);
}class LookSchedulingStrategy implements SchedulingStrategy {// 实现LOOK算法@Overridepublic Integer getNextFloor(int currentFloor, Direction direction, List<Passenger> internal, List<Passenger> external) {// 更高效的调度逻辑}
}class ScanSchedulingStrategy implements SchedulingStrategy {// 实现SCAN算法
}
- 改进请求队列设计
class RequestQueue {private PriorityQueue<Passenger> internalRequests;private PriorityQueue<Passenger> externalRequests;public RequestQueue(Comparator<Passenger> comparator) {this.internalRequests = new PriorityQueue<>(comparator);this.externalRequests = new PriorityQueue<>(comparator);}// 添加基于方向的查询方法public List<Passenger> getRequestsInDirection(int currentFloor, Direction direction) {// 返回指定方向的所有请求,而不仅仅是第一个}
}
- 分解复杂方法
点击查看代码
class Controller {// 将moveToNextTarget分解private void moveToFloor(int targetFloor) {while (elevator.getCurrentFloor() != targetFloor) {moveOneStep(targetFloor);checkIntermediateStops();}processArrival(targetFloor);}private void moveOneStep(int targetFloor) {int current = elevator.getCurrentFloor();elevator.setCurrentFloor(targetFloor > current ? current + 1 : current - 1);elevator.setDirection(targetFloor > current ? Direction.UP : Direction.DOWN);}private void checkIntermediateStops() {if (shouldStop(elevator.getCurrentFloor())) {processStop();}}
}
- 改进方向判断逻辑
class Controller {public void determineDirection() {if (queue.isEmpty()) {elevator.setDirection(Direction.IDLE);return;}Direction currentDirection = elevator.getDirection();if (hasRequestsInCurrentDirection()) {return; // 保持原方向}Direction oppositeDirection = getOppositeDirection(currentDirection);if (hasRequestsInDirection(oppositeDirection)) {elevator.setDirection(oppositeDirection);return;}// 默认逻辑elevator.setDirection(getDefaultDirection());}private boolean hasRequestsInCurrentDirection() {return queue.hasRequestsInDirection(elevator.getCurrentFloor(), elevator.getDirection());}
}
五、总结
通过这三次迭代性题目集的综合性学习,我收获到了很多,首先,理解题目电梯调度的算法并且成功用Java编程实现出来,使我的逻辑思维和编程能力有了大幅度的提升。然后在不断编写和调试代码的过程中, 对于Java的语法掌握、正则表达式的灵活应用和面向对象的程序设计有了全方面的理解和提升,尤其是对于类的设计以及对类间关系的理解这一块,在通过第一个题目集迭代到第二个题目集解决电梯类职责过多的问题的训练,现在我对于类设计的遵循单一职责原则和类间关系的设计有了更加深刻的认识。但还是需要对可扩展性的设计结构进行深入的研究和学习。