(1)前言
这三次迭代的电梯调度程序设计题目,难度呈逐次上升.第一次聚焦基础,让我熟悉了电梯运行的基本逻辑和面向对象的简单应用,初步构建起电梯类及相关方法。第二次强调设计原则,运用单一职责原则拆分功能模块,处理无效和重复请求,促使我对代码结构优化有更深入理解。第三次迭代进一步深化,引入乘客类,改变请求输入格式与处理逻辑,增加了外部请求到内部请求的转换。经过这三次练习,我对电梯运行过程的逻辑把握愈发清晰,对面向对象编程思想及设计原则的应用也更加熟练。以下是对三次迭代的具体分析:
第一次迭代
题目
设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。
请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求),具体运行规则详见输入输出样例。
输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
- 电梯内乘客请求格式:<楼层数>
- 电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
- 当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:
- 运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向 - 运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
知识点:主要考察面向对象编程基础,涉及类的设计、属性与方法的定义。需要设计一个电梯类,包含电梯的基本属性(如最大楼层数、最小楼层数、当前楼层、运行方向、运行状态等)和方法,以及请求队列的管理和简单的调度算法。
题量:单个题目,重点在于设计一个完整的电梯类来模拟电梯运行。
难度:相对较低,主要是对面向对象编程基础概念的应用,如类的封装、方法的实现等。
用到的类和方法:
类:Elevator(电梯类)。
方法:构造方法用于初始化电梯属性;addRequest 方法用于添加请求到队列;run 方法实现电梯的运行逻辑,包括移动、开门、关门等操作。
第二次迭代
题目
对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客请求类、队列类以及控制类,具体设计可参考如下类图。
电梯运行规则与前阶段单类设计相同,但要处理如下情况:
- 乘客请求楼层数有误,具体为高于最高楼层数或低于最低楼层数,处理方法:程序自动忽略此类输入,继续执行
- 乘客请求不合理,具体为输入时出现连续的相同请求,例如<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
知识点:强调单一职责原则(SRP)的应用,将不同的功能模块拆分成不同的类,如乘客请求类、队列类、控制类等,以提高代码的可维护性和可扩展性。同时,需要处理无效请求和重复请求。
题量:单个题目,但需要对第一次迭代的代码进行重构,设计多个类并协调它们之间的关系。
难度:中等,需要理解并应用单一职责原则,对代码结构进行优化,同时处理一些异常情况。
用到的类和方法:
类:Elevator(电梯类)、PassengerRequest(乘客请求类)、RequestQueue(请求队列类)、ElevatorController(控制类)。
方法:PassengerRequest 类的构造方法用于初始化请求信息;RequestQueue 类的 addRequest 方法用于添加请求并处理重复请求;ElevatorController 类的 dispatch 方法实现电梯的调度逻辑。
第三次迭代
题目
对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客类、队列类以及控制类,具体设计可参考如下类图。
电梯运行规则与前阶段相同,但有如下变动情况:
- 乘客请求输入变动情况:外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
- 对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)
输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
- 电梯内乘客请求格式:<楼层数>
- 电梯外乘客请求格式:<请求源楼层,请求目的楼层>,其中,请求源楼层表示乘客发起请求所在的楼层,请求目的楼层表示乘客想要到达的楼层。
- 当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:
- 运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向 - 运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
知识点:进一步深化面向对象编程,加入乘客类,取消乘客请求类,重新设计请求的输入格式和处理逻辑。同时,需要处理外部请求和内部请求的转换。
题量:单个题目,需要对第二次迭代的代码进行再次重构,设计新的类和调整请求处理逻辑。
难度:较高,涉及到类的重新设计和请求处理逻辑的复杂调整,需要对面向对象编程有更深入的理解。
用到的类和方法:
类:Elevator(电梯类)、Passenger(乘客类)、RequestQueue(请求队列类)、ElevatorController(控制类)。
方法:Passenger 类的构造方法用于初始化乘客信息;RequestQueue 类的 addExternalRequest 方法用于添加外部请求并将目的楼层加入内部队列;ElevatorController 类的 processRequest 方法实现请求的处理和电梯的调度。
(2)设计与分析
大体思路:
先确定电梯类的结构。比如minFloor、maxFloor、currentFloor,方向direction,状态status。内部请求队列可能是一个列表,记录目标楼层。外部请求队列需要分开上行和下行。
然后是状态管理。电梯的状态可能有停止、开门、关门。需要用枚举类型来定义状态和方向。
接下来是请求队列的管理。内部请求就是乘客在电梯内按下的目标楼层,外部请求是楼层和方向的组合。需要区分处理。当电梯移动时,要检查当前方向是否有请求,优先处理同方向的。
输入处理部分,需要读取用户输入,解析每个请求。
输出方面,每次移动一层要输出当前楼层和方向,如果需要停靠,则输出开门和关门的信息。
电梯类Elevator。属性包括:
- minFloor, maxFloor
- currentFloor,初始为minFloor(默认为1)
- direction:枚举,UP、DOWN、STOP(初始停止)
请求队列:
- 内部请求队列:可以用一个列表或队列,记录目标楼层。
- 外部请求队列:有上行请求和一个下行请求。
调度算法思路:
- 确定当前方向。初始方向为停止,当有请求时选择方向。
- 处理当前方向的所有可能的请求。
- 当没有同方向的请求时,切换方向,处理反方向的请求。
- 重复直到所有请求处理完毕。
每次移动一层,然后检查该层是否需要停靠。停靠的条件是:该层在内部请求队列中,或者该层有外部请求且方向匹配当前电梯方向。
当电梯移动时,每到达一个楼层,检查:
- 是否有内部请求的目标楼层等于当前楼层。
- 是否有外部上行请求在该楼层。
如果有任一满足,则停靠,处理这些请求(移除它们),并输出开门关门。
第一次迭代代码分析

第一次迭代的代码将电梯相关功能集中在一个 Elevator 类中,包含了电梯属性定义、请求处理与运行逻辑。代码通过两个队列 interRequire 和 externalRequests 分别存储内部和外部请求,Elevator 类的构造方法初始化电梯的最小楼层、最大楼层、当前楼层和运行方向。
在方法设计上,addInterRequire 和 addExterRequire 负责添加内外部请求,并进行楼层有效性检查。核心的 run 方法实现电梯运行逻辑,通过 determineDirection 方法确定运行方向,优先处理同方向请求,利用 findNextUpFloor 和 findNextDownFloor 找到下一个要到达的楼层,handleAllStops 方法处理当前楼层的所有请求,包括开关门操作。
第二次迭代代码分析


第二次迭代遵循了单一职责原则,将功能拆分为 Elevator、RequestQueue、Controller 等多个类。Elevator 类负责电梯状态和属性管理;RequestQueue 类处理请求队列,包括添加请求和去重逻辑;Controller 类作为控制核心,协调电梯运行和请求处理。
Controller 类的 processRequest 方法是程序核心,通过 determineDirection 方法确定电梯运行方向,优先处理同方向请求,当同方向无请求时检查反向请求。getNextFloor 方法找到下一个目标楼层,move 方法控制电梯移动,openDoors 和 removeRequest 方法处理到达楼层的开关门和请求移除操作。
第三次迭代代码分析



第三次迭代在第二次基础上引入 Passenger 类,改变外部请求格式,将外部请求的目的楼层加入内部请求队列。RequestQueue 类新增处理外部请求的方法,Controller 类的请求处理逻辑也相应调整。
RequestQueue 类通过 addExternalRequest 方法添加外部请求,并将目的楼层加入内部请求队列,getClosestExternalFloor 和 getClosestInternalFloor 方法分别找到最近的外部和内部目标楼层。Controller 类的 processRequests 方法作为主逻辑,通过 determineDirection 确定方向,moveElevator 控制电梯移动,shouldStop 方法判断是否需要在某楼层停靠,openDoors 和 closeDoors 处理开关门操作。
(3)采坑心得
说实话,从运行超时到非零返回到答案错误,一直在坑里T-T


电梯的调度算法
第一次迭代中,一开始我搞错了算法。电梯通常有两种模式:一种是在一个方向处理完所有请求后再改变方向(如SCAN算法),另一种是根据最近的请求动态调整方向。题目要求方向优先,应当使用SCAN算法,这样电梯会持续一个方向直到该方向没有请求,然后反转方向。
运行超时
方向判断时循环。在determineDirection方法中,当电梯处于停止状态时,会检查是否有上行或下行请求。但是当电梯处理完当前方向的请求后,没有正确切换到相反方向,或者没有处理所有请求导致无限循环。
解决方案:
在每次移动后,重新评估当前方向是否还有请求,如果没有,切换方向或停止。
边界情况未被正确处理
测试有输出结果但是提交了运行超时,某些边界情况未被正确处理,电梯到达最高或最低楼层时,未正确改变方向,导致进入无限循环。
addExternalRequest方法的内部请求添加逻辑
在RequestQueue的addExternalRequest方法中,当前的做法是:
public void addExternalRequest(Passenger passenger) {
externalRequests.add(passenger);
internalRequests.add(new Passenger(null, passenger.getDestinationFloor()));
}
在第三次的代码中,当调用addExternalRequest时,立即添加内部请求到内部队列是错误的。这可能导致内部请求在乘客还没进入电梯时就被处理,从而导致电梯提前改变方向或者错误地处理请求。正确的逻辑应该是,当电梯到达外部请求的源楼层时,才会将对应的内部请求添加到队列中。所以,在当前的代码中,addExternalRequest方法中的内部请求添加是过早的,应该被移除,改为在电梯到达外部请求的楼层时,再添加内部请求。我还说我的电梯怎么上去了就不下来了呢......
因此,修正这个问题的方法是:在添加外部请求时,不应同时添加内部请求,而是应该在电梯到达外部请求的源楼层时,再将内部请求添加到队列中。
修改步骤:
-
在RequestQueue的addExternalRequest方法中,不再添加内部请求。即删除该行:internalRequests.add(new Passenger(...));
-
当电梯到达某个楼层处理外部请求时,将该乘客的目的地楼层作为内部请求添加到内部队列。
(4)改进建议
代码缺乏注释,可读性较差,不利于后续修改和扩展。
部分方法命名不够直观,方法的功能从命名上较难直接理解。
类之间的依赖关系可以进一步优化,部分方法逻辑可以抽取为更小的方法,提高代码复用性。
部分方法逻辑较为复杂,如 determineDirection 方法,可进一步拆分优化。
类之间的交互可以通过设计模式进一步优化,提高代码的可扩展性和可维护性。
部分边界情况处理不够完善,如极端输入情况下的程序稳定性有待加强。
(5)总结
浙公网安备 33010602011771号