第一次blog-对题目集5~7电梯调度分析
前言
一、知识点总结
三次题目集围绕电梯调度程序展开,逐步迭代优化,涉及的核心知识点如下:
题目集 5:基础单类实现
面向过程与基础类设计:以ElevatorSystem类为核心,包含楼层配置、请求存储、状态管理等功能。使用数组存储请求队列,通过int类型标识方向(1 - 上行,2 - 下行)。
输入输出处理:通过Scanner读取输入,区分楼层限制设置(纯数字)和请求指令(以<开头),处理end结束指令。
调度逻辑:遵循 “同方向优先” 原则,通过operationStatus控制运行方向,移动时逐个楼层检查请求,停靠时处理同楼层请求并更新方向。
题目集 6:类职责拆分(单一职责原则)
类设计优化:
电梯类(Lift):封装当前楼层、方向、上下限等属性,提供移动(ascend/descend)和方向设置方法。
请求队列类(ReqQueue):使用LinkedList分别存储内部请求(整数)和外部请求(ExternalCall对象,包含楼层和方向)。
控制类(ControlUnit):负责调度逻辑,分离输入处理(handleInput)和运行逻辑(operateLift)。
数据结构升级:用链表替代数组,便于动态增删请求,支持去重处理(如忽略连续相同请求)。
枚举类型应用:定义MoveDirection枚举(UP/DOWN),替代原始数字标识,增强代码可读性。
题目集 7:引入乘客类与请求流程重构
乘客类(Passenger):封装源楼层和目的楼层,外部请求处理后自动将目的楼层加入内部队列。
请求队列泛型化:ReqQueue存储Passenger对象,统一内外请求格式,外部请求通过getSourceFloor()和getDirection()获取方向。
调度逻辑调整:处理外部请求时,需将乘客转入内部队列,方向判断基于乘客的完整行程(源→目的)。
二、题量与难度分析
| 题目集 | 题量(代码行数) | 核心难度点 | 适合阶段 |
|---|---|---|---|
| 题目集 5 | ~200 行 | 单类逻辑复杂度高,数组索引管理繁琐 | 难 |
| 题目集 6 | ~300 行 | 类间协作与职责分离,链表去重逻辑 | 难 |
| 题目集 7 | ~400 行 | 乘客状态管理,请求流程跨类传递 | 较易 |
三、题量递增:
从单类实现到多类协作,代码量随功能拆分和逻辑细化逐步增加,题目集 7 因引入乘客类和请求转换逻辑,代码量达到峰值。
四、难度梯度:
题目集 5 的难点在于单类内逻辑的耦合性,如请求存储与调度逻辑混合,易导致代码臃肿。
题目集 6 强调类设计原则(SRP),需合理拆分职责,处理链表去重和请求优先级问题。
题目集 7 进一步深化面向对象设计,要求理解乘客生命周期(外部请求→进入电梯→内部请求),涉及队列转换和状态跟踪。
题目集5与其代码展示:
点击查看题目集5题目
设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。
请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求),具体运行规则详见输入输出样例。
输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
• 电梯内乘客请求格式:<楼层数>
• 电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
• 当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:
• 运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
• 运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
点击查看题目集5代码
import java.util.Scanner;
public class Main {
public static void main(String[] executionParameters) {
ElevatorSystem elevatorSystem = new ElevatorSystem(0, -10000, 1);
elevatorSystem.initializeSettings();
elevatorSystem.startOperation();
}
}
class ElevatorSystem {
// 配置参数
private int maxFloor;
private int minFloor;
private int currentFloor = 1;
// 运行状态
private int operationStatus = 0;
// 0:静止 1:上行 2:下行
private int operationMode = 0;
// 请求存储
private int[][] elevatorRequests = new int[2][1000];
private int[] requestDirection = new int[1000];
private int[] pendingRequestCounts = new int[2];
ElevatorSystem(int upperLimit, int lowerLimit, int initialStatus) {
this.maxFloor = upperLimit;
this.minFloor = lowerLimit;
this.operationMode = initialStatus;
this.pendingRequestCounts[0] = 0;
this.pendingRequestCounts[1] = 0;
}
public void initializeSettings() {
Scanner inputScanner = new Scanner(System.in);
while (inputScanner.hasNextLine()) {
String inputLine = inputScanner.nextLine().trim();
if (inputLine.equalsIgnoreCase("end")) {
stopOperation();
break;
}
if (inputLine.matches("\\d+")) {
setFloorLimits(Integer.parseInt(inputLine));
continue;
}
if (inputLine.startsWith("<")) {
processElevatorRequest(inputLine);
}
}
}
private void setFloorLimits(int value) {
if (minFloor == -10000) {
minFloor = value;
} else {
maxFloor = value;
}
}
private void processElevatorRequest(String request) {
String requestContent = request.substring(1, request.length() - 1);
String[] parts = requestContent.split(",");
if (parts.length > 1) {
recordExternalRequest(
Integer.parseInt(parts[0].trim()),
parts[1].trim().toUpperCase()
);
} else {
recordInternalRequest(Integer.parseInt(requestContent.trim()));
}
}
private void recordExternalRequest(int floor, String direction) {
elevatorRequests[0][pendingRequestCounts[0]] = floor;
requestDirection[pendingRequestCounts[0]] = direction.equals("UP") ? 1 : 2;
pendingRequestCounts[0]++;
}
private void recordInternalRequest(int destination) {
elevatorRequests[1][pendingRequestCounts[1]] = destination;
pendingRequestCounts[1]++;
}
public void startOperation() {
int externalRequestIndex = 0;
int internalRequestIndex = 0;
setOperationDirection(1);
startElevator();
while (hasPendingRequests(externalRequestIndex, internalRequestIndex)) {
showElevatorStatus();
while (hasExternalRequestAtCurrentFloor(externalRequestIndex, internalRequestIndex)) {
openAndCloseDoor(currentFloor);
externalRequestIndex++;
updateOperationDirection(externalRequestIndex, internalRequestIndex);
}
while (hasInternalRequestAtCurrentFloor(externalRequestIndex, internalRequestIndex)) {
if (currentFloor != elevatorRequests[0][externalRequestIndex]) {
openAndCloseDoor(currentFloor);
}
internalRequestIndex++;
updateOperationDirection(externalRequestIndex, internalRequestIndex);
}
moveElevator();
}
}
private boolean hasPendingRequests(int extIndex, int intIndex) {
return extIndex < pendingRequestCounts[0] || intIndex < pendingRequestCounts[1];
}
private void showElevatorStatus() {
String direction = operationStatus == 1 ? "UP" : "DOWN";
System.out.printf("Current Floor: %d Direction: %s\n", currentFloor, direction);
}
private boolean hasExternalRequestAtCurrentFloor(int extIndex, int intIndex) {
return currentFloor == elevatorRequests[0][extIndex] &&
extIndex < pendingRequestCounts[0] &&
(operationStatus == requestDirection[extIndex] ||
intIndex == pendingRequestCounts[1]);
}
private boolean hasInternalRequestAtCurrentFloor(int extIndex, int intIndex) {
return currentFloor == elevatorRequests[1][intIndex] &&
intIndex < pendingRequestCounts[1];
}
private void openAndCloseDoor(int floor) {
System.out.printf("Open Door # Floor %d\nClose Door\n", floor);
}
private void updateOperationDirection(int extIndex, int intIndex) {
if (extIndex == pendingRequestCounts[0]) {
setOperationDirection(currentFloor > elevatorRequests[1][intIndex] ? 2 : 1);
} else if (intIndex == pendingRequestCounts[1]) {
setOperationDirection(currentFloor > elevatorRequests[0][extIndex] ? 2 : 1);
} else {
boolean needToGoDown = currentFloor > elevatorRequests[0][extIndex] &&
currentFloor > elevatorRequests[1][intIndex];
boolean needToGoUp = currentFloor < elevatorRequests[0][extIndex] &&
currentFloor < elevatorRequests[1][intIndex];
setOperationDirection(needToGoDown ? 2 : needToGoUp ? 1 : operationStatus);
}
}
private void moveElevator() {
currentFloor += (operationStatus == 1) ? 1 : -1;
}
private void setOperationDirection(int newDirection) {
this.operationStatus = newDirection;
}
private void startElevator() {
this.operationMode = 1;
}
private void stopOperation() {
this.operationMode = 0;
}
}
题目集5分析:
1.分析图例:

2.分析数据:
点击查看数据
Metrics Details For File 'Elevator1.java'
--------------------------------------------------------------------------------------------
Parameter Value
========= =====
Project Directory C:\JavaElevator\
Project Name SMTest2
Checkpoint Name Baseline2
File Name Elevator1.java
Lines 170
Statements 93
Percent Branch Statements 16.1
Method Call Statements 34
Percent Lines with Comments 2.4
Classes and Interfaces 2
Methods per Class 9.00
Average Statements per Method 3.61
Line Number of Most Complex Method 132
Name of Most Complex Method ElevatorSystem.updateOperationDirection()
Maximum Complexity 8
Line Number of Deepest Block 97
Maximum Block Depth 5
Average Block Depth 2.06
Average Complexity 2.44
--------------------------------------------------------------------------------------------
Most Complex Methods in 2 Class(es): Complexity, Statements, Max Depth, Calls
ElevatorSystem.ElevatorSystem() 1, 5, 2, 0
ElevatorSystem.hasExternalRequestAtCurrentFloor() 3, 1, 2, 0
ElevatorSystem.hasInternalRequestAtCurrentFloor() 2, 1, 2, 0
ElevatorSystem.hasPendingRequests() 2, 1, 2, 0
ElevatorSystem.initializeSettings() 5, 9, 4, 9
ElevatorSystem.moveElevator() 2, 1, 2, 0
ElevatorSystem.openAndCloseDoor() 1, 1, 2, 1
ElevatorSystem.processElevatorRequest() 3, 6, 3, 7
ElevatorSystem.recordExternalRequest() 2, 3, 2, 1
ElevatorSystem.recordInternalRequest() 1, 2, 2, 0
ElevatorSystem.setFloorLimits() 3, 4, 3, 0
ElevatorSystem.setOperationDirection() 1, 1, 2, 0
ElevatorSystem.showElevatorStatus() 2, 2, 2, 1
ElevatorSystem.startElevator() 1, 1, 2, 0
ElevatorSystem.startOperation() 5, 16, 5, 11
ElevatorSystem.stopOperation() 1, 1, 2, 0
ElevatorSystem.updateOperationDirection() 8, 7, 3, 2
Main.main() 1, 3, 2, 2
--------------------------------------------------------------------------------------------
Block Depth Statements
0 3
1 25
2 39
3 16
4 9
5 1
6 0
7 0
8 0
9+ 0
--------------------------------------------------------------------------------------------
3.分析解读:
(1)基本信息:
行数:170
语句数:93
分支语句百分比:16.1%
方法调用语句数:34
带注释行的百分比:2.4%
从这些信息可知,代码规模相对较小,但注释较少,不利于后续的维护和理解。分支语句占比相对较低,表明代码的逻辑分支不算复杂。
(2)类和方法信息:
类和接口数量:2 个,分别是ElevatorSystem和Main。
每个类的方法数:平均每个类有 9 个方法,说明类的功能较为丰富,但也可能存在职责不够单一的问题。
每个方法的平均语句数:3.61 条,表明方法相对简洁,但也可能意味着部分功能被拆分得过细。
(3)复杂度分析:
1'最复杂方法
最复杂方法的行号:132
最复杂方法的名称:ElevatorSystem.updateOperationDirection()
最大复杂度:8
2'平均复杂度
平均复杂度:2.44
整体代码的平均复杂度较低,说明大部分方法的逻辑相对简单,但updateOperationDirection方法拉高了整体的复杂度。
3'代码块深度分析
最深代码块的行号:97
最大代码块深度:5
平均代码块深度:2.06
最大代码块深度为 5,结合源代码,是在startOperation方法中存在多层嵌套的while循环导致的
我的思路与心得:
我的类图:

代码思路分析
整体架构搭建思路:
1.我设计了Main类作为程序入口,以及核心的ElevatorSystem类来实现电梯系统的各种功能。将电梯系统看作一个整体对象,ElevatorSystem类负责封装电梯的所有属性(如楼层限制、当前楼层、运行状态等)和行为(如处理请求、移动电梯等) ,Main类仅用于创建ElevatorSystem对象并启动相关操作,体现了初步的面向对象编程思想,把系统功能模块化,便于管理和维护。
参数与属性定义思路
在ElevatorSystem类中定义了一系列属性来描述电梯系统的状态和请求信息。比如用maxFloor和minFloor确定楼层范围,currentFloor记录当前所在楼层,operationStatus和operationMode分别表示运行方向和运行模式 。对于请求相关的属性,使用二维数组elevatorRequests分别存储外部和内部请求(第一维 0 表示外部请求,1 表示内部请求 ),requestDirection记录外部请求的方向,pendingRequestCounts记录未处理的内、外部请求数量。这种属性定义方式,是基于对电梯运行过程中所涉及信息的梳理,将各种数据分类存储,方便后续在不同方法中调用和处理。
2.功能实现思路
设置初始化:initializeSettings方法实现了从控制台读取输入信息的功能。思路是通过Scanner不断读取用户输入,根据输入内容的特征进行判断。如果是 “end”,则停止操作;如果是纯数字,就设置楼层限制;如果是以 “<” 开头,则视为电梯请求进行处理。这是按照实际电梯系统使用场景来设计的,先获取系统运行的基本参数(楼层范围),再处理用户发出的请求指令。
请求处理:processElevatorRequest方法对输入的请求字符串进行解析。根据请求字符串是否包含逗号,区分是外部请求还是内部请求,然后分别调用recordExternalRequest和recordInternalRequest方法记录请求。这种根据请求格式进行分类处理的方式,符合电梯系统实际接收不同类型请求的逻辑,将请求处理流程进行了细化和规范。
运行逻辑:startOperation方法是电梯系统运行的核心逻辑。首先初始化请求索引,设置运行方向并启动电梯。然后在存在未处理请求的情况下,不断循环。每次循环先展示电梯状态,接着分别处理当前楼层的外部和内部请求(如果有),处理完请求后移动电梯。其中判断是否有外部或内部请求在当前楼层的方法(hasExternalRequestAtCurrentFloor和hasInternalRequestAtCurrentFloor ),以及更新运行方向的updateOperationDirection方法,都是围绕电梯实际运行中 “停靠 - 处理请求 - 移动” 的流程来设计的,通过一系列条件判断来实现电梯运行逻辑的模拟。
我的实现:


!!!错误分析:
对于下一楼层逻辑拓展性 不完善,仅判断了是否有更高楼层请求,如果需求扩展到判断更低楼层请求或者更复杂的请求优先级逻辑,这段代码不能解决问题。
!!!处理错误:
重新考虑两个对头元素,优先方向,当向上时没有最高楼层时电梯转向,当向下时没有最低楼层时电梯转向(限于还有楼层请求),增加电梯向下时的楼层找寻
心得总结:
面向对象编程的初步实践体会
通过这次代码编写,对面向对象编程有了更深刻的认识。将电梯系统抽象成一个类,把相关的数据和操作封装在一起,使得代码结构更加清晰。不同的功能由不同的方法实现,每个方法专注于完成一个特定任务,例如处理请求、更新方向等,增强了代码的可读性和可维护性。但在实践过程中也发现,类中的方法和属性较多,在方法之间传递参数以及协调各方法之间的逻辑关系时,需要仔细规划,否则容易导致代码逻辑混乱。
输入输出处理的思考
在处理输入输出时,通过Scanner读取控制台输入并进行解析,这种方式虽然简单直接,但也存在一些局限性。比如没有对输入数据进行更严格的校验,像输入的楼层值可能不符合实际范围等情况没有充分处理。在后续的开发中,应该加强输入数据的合法性校验,以提高程序的健壮性。同时,输出方面只是简单地打印电梯状态和开关门信息,如果要进一步优化用户体验,可能需要考虑更友好的输出格式或者增加图形化界面展示等。
逻辑实现的挑战与收获
实现电梯运行逻辑的过程中,遇到了不少挑战:在判断电梯运行方向和处理请求优先级时,需要考虑多种情况,像外部请求和内部请求同时存在时如何决策,不同方向的请求如何处理等。通过不断调试和优化updateOperationDirection等方法,逐渐理清了逻辑。这让我明白在处理复杂逻辑时,要对实际问题进行深入分析,将大问题拆解成多个小问题逐步解决,并且要多进行测试,考虑各种边界情况,才能确保程序的正确性和稳定性。
题目集6与其代码展示:
点击查看题目集6题目
对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客请求类、队列类以及控制类
电梯运行规则与前阶段单类设计相同,但要处理如下情况:
乘客请求楼层数有误,具体为高于最高楼层数或低于最低楼层数,处理方法:程序自动忽略此类输入,继续执行
乘客请求不合理,具体为输入时出现连续的相同请求,例如<3><3><3>或者<5,DOWN><5,DOWN>,处理方法:程序自动忽略相同的多余输入,继续执行,例如<3><3><3>过滤为<3>
注意:本次作业类设计必须符合如上要求(包含但不限于乘客请求类、电梯类、请求队列类及控制类,其中控制类专门负责电梯调度过程),凡是不符合类设计要求此题不得分,另外,PTA得分代码界定为第一次提交的最高分代码(因此千万不要把第一次电梯程序提交到本次题目中测试)。
输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:
运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
点击查看题目集6代码
import java.util.Scanner;
import java.util.LinkedList;
enum MoveDirection {
UP, DOWN
}
enum State {
MOVING, STOPPING;
}
class Passenger{
}
class Lift {
private int currentLevel = 1;
private MoveDirection dir = MoveDirection.UP;
private int topFloor;
private int baseFloor;
public Lift(int baseFloor, int topFloor) {
this.baseFloor = baseFloor;
this.topFloor = topFloor;
}
public void ascend() { currentLevel++; }
public void descend() { currentLevel--; }
public void setDirUp() { this.dir = MoveDirection.UP; }
public void setDirDown() { this.dir = MoveDirection.DOWN; }
public int getCurrentLevel() { return this.currentLevel; }
public MoveDirection getDirection() { return this.dir; }
public void setDirection(MoveDirection d) { this.dir = d; }
public int getTopFloor() { return this.topFloor; }
public int getBaseFloor() { return this.baseFloor; }
}
class ExternalCall {
private Integer level;
private MoveDirection dir;
public ExternalCall(Integer level, MoveDirection dir) {
this.level = level;
this.dir = dir;
}
public Integer getLevel() { return this.level; }
public MoveDirection getDir() { return this.dir; }
}
class ReqQueue {
private LinkedList<Integer> interiorCalls = new LinkedList<>();
private LinkedList<ExternalCall> exteriorCalls = new LinkedList<>();
public void addInteriorCall(int level) { interiorCalls.add(level); }
public void addExteriorCall(int level, MoveDirection d) {
exteriorCalls.add(new ExternalCall(level, d));
}
public LinkedList<Integer> getInteriorCalls() { return this.interiorCalls; }
public LinkedList<ExternalCall> getExteriorCalls() { return this.exteriorCalls; }
}
class ControlUnit {
private Lift lift;
private ReqQueue requests;
public ControlUnit() {
this.lift = new Lift(1, 10);
this.requests = new ReqQueue();
}
private void adjustDirection(int currLevel, MoveDirection currDir,
Integer extLevel, MoveDirection extDir, Integer intLevel) {
LinkedList<Integer> interior = requests.getInteriorCalls();
LinkedList<ExternalCall> exterior = requests.getExteriorCalls();
if (exterior.isEmpty()) {
if (currLevel > intLevel) lift.setDirDown();
else if (currLevel < intLevel) lift.setDirUp();
} else if (interior.isEmpty()) {
if (currLevel > extLevel) lift.setDirDown();
else if (currLevel < extLevel) lift.setDirUp();
} else if (currLevel > extLevel && currLevel > intLevel) {
lift.setDirDown();
} else if (currLevel < extLevel && currLevel < intLevel) {
lift.setDirUp();
}
}
private int processExterior(int currLevel, MoveDirection currDir, Integer extLevel, MoveDirection extDir, Integer intLevel) {
LinkedList<ExternalCall> exterior = requests.getExteriorCalls();
if (!exterior.isEmpty()) {
if (currLevel == extLevel) {
if (currDir == extDir || requests.getInteriorCalls().isEmpty()) {
System.out.printf("Open Door # Floor %d\nClose Door\n", currLevel);
return 1;
} else if (currDir != extDir) {
if (currDir == MoveDirection.UP && currLevel > intLevel) {
lift.setDirDown();
System.out.printf("Open Door # Floor %d\nClose Door\n", currLevel);
return 1;
} else if (currDir == MoveDirection.DOWN && currLevel < intLevel) {
lift.setDirUp();
System.out.printf("Open Door # Floor %d\nClose Door\n", currLevel);
return 1;
}
}
}
}
return 0;
}
private int processInterior(int currLevel, MoveDirection currDir,
Integer extLevel, MoveDirection extDir, Integer intLevel) {
LinkedList<Integer> interior = requests.getInteriorCalls();
if (interior.isEmpty()) return 0;
if (currLevel == intLevel) {
if (currLevel != extLevel) {
System.out.printf("Open Door # Floor %d\nClose Door\n", currLevel);
}
return 1;
}
return 0;
}
public void operateLift() {
while (true) {
Integer extLevel = 0;
MoveDirection extDir = MoveDirection.UP;
LinkedList<Integer> interior = requests.getInteriorCalls();
LinkedList<ExternalCall> exterior = requests.getExteriorCalls();
int currLevel = lift.getCurrentLevel();
MoveDirection currDir = lift.getDirection();
Integer intLevel;
ExternalCall extCall = exterior.peek();
if (!exterior.isEmpty()) {
extLevel = extCall.getLevel();
extDir = extCall.getDir();
}
System.out.printf("Current Floor: %d Direction: %s\n",
currLevel, currDir.toString());
while (true) {
currDir = lift.getDirection();
intLevel = interior.peek();
extCall = exterior.peek();
if (!exterior.isEmpty()) {
extLevel = extCall.getLevel();
extDir = extCall.getDir();
}
boolean handled = false;
if (!interior.isEmpty() && !exterior.isEmpty()) {
if (currLevel == extLevel && currLevel == intLevel) {
if (processExterior(currLevel, currDir, extLevel, extDir, intLevel) == 1) {
interior.poll();
exterior.poll();
handled = true;
}
}
}
if (!handled && !exterior.isEmpty()) {
if (processExterior(currLevel, currDir, extLevel, extDir, intLevel) == 1) {
exterior.poll();
handled = true;
}
}
if (!handled && !interior.isEmpty()) {
if (processInterior(currLevel, currDir, extLevel, extDir, intLevel) == 1) {
interior.poll();
adjustDirection(currLevel, currDir, extLevel, extDir, intLevel);
handled = true;
}
}
if (!handled) break;
}
if (interior.isEmpty() && exterior.isEmpty()) break;
adjustDirection(currLevel, currDir, extLevel, extDir, intLevel);
MoveDirection newDir = lift.getDirection();
if (newDir == MoveDirection.UP) lift.ascend();
if (newDir == MoveDirection.DOWN) lift.descend();
}
}
public void handleInput() {
Scanner inputScanner = new Scanner(System.in);
int base = 0, top = 0;
int inputCounter = 0, prevExtLevel = 0, prevExtDirCode = 0, prevIntLevel = 0;
while (inputScanner.hasNextLine()) {
String line = inputScanner.nextLine().trim();
if (line.equalsIgnoreCase("end")) break;
if (line.matches("\\d+")) {
if (inputCounter++ == 0) base = Integer.parseInt(line);
else {
top = Integer.parseInt(line);
lift = new Lift(base, top);
}
continue;
}
if (line.startsWith("<") && line.endsWith(">")) {
String content = line.substring(1, line.length()-1);
String[] parts = content.split(",");
if (parts.length == 2) {
int lv = Integer.parseInt(parts[0].trim());
if (lv < base || lv > top) continue;
String dirPart = parts[1].trim().toUpperCase();
MoveDirection md = dirPart.equals("DOWN") ? MoveDirection.DOWN : MoveDirection.UP;
if (!(prevExtLevel == lv && prevExtDirCode == md.ordinal())) {
requests.addExteriorCall(lv, md);
prevExtLevel = lv;
prevExtDirCode = md.ordinal();
}
} else {
int lv = Integer.parseInt(content.trim());
if (lv >= base && lv <= top && lv != prevIntLevel) {
requests.addInteriorCall(lv);
prevIntLevel = lv;
}
}
}
}
inputScanner.close();
}
}
public class Main {
public static void main(String[] args) {
ControlUnit control = new ControlUnit();
control.handleInput();
control.operateLift();
}
}
题目集6分析:
1.分析图例:

2.分析数据:
点击查看数据
Metrics Details For File 'Elevator2.java'
--------------------------------------------------------------------------------------------
Parameter Value
========= =====
Project Directory C:\JavaElevator\
Project Name SMTest2
Checkpoint Name Baseline2
File Name Elevator2.java
Lines 214
Statements 183
Percent Branch Statements 23.5
Method Call Statements 79
Percent Lines with Comments 0.0
Classes and Interfaces 8
Methods per Class 3.00
Average Statements per Method 5.75
Line Number of Most Complex Method 111
Name of Most Complex Method ControlUnit.operateLift()
Maximum Complexity 21
Line Number of Deepest Block 139
Maximum Block Depth 7
Average Block Depth 2.87
Average Complexity 3.46
--------------------------------------------------------------------------------------------
Most Complex Methods in 5 Class(es): Complexity, Statements, Max Depth, Calls
ControlUnit.adjustDirection() 11, 16, 3, 10
ControlUnit.ControlUnit() 1, 2, 2, 0
ControlUnit.handleInput() 18, 30, 6, 18
ControlUnit.operateLift() 21, 44, 7, 33
ControlUnit.processExterior() 10, 16, 6, 9
ControlUnit.processInterior() 4, 8, 4, 3
ExternalCall.ExternalCall() 1, 2, 2, 0
ExternalCall.getDir() 1, 1, 2, 0
ExternalCall.getLevel() 1, 1, 2, 0
Lift.ascend() 1, 1, 2, 0
Lift.descend() 1, 1, 2, 0
Lift.getBaseFloor() 1, 1, 2, 0
Lift.getCurrentLevel() 1, 1, 2, 0
Lift.getDirection() 1, 1, 2, 0
Lift.getTopFloor() 1, 1, 2, 0
Lift.Lift() 1, 2, 2, 0
Lift.setDirDown() 1, 1, 2, 0
Lift.setDirection() 1, 1, 2, 0
Lift.setDirUp() 1, 1, 2, 0
Main.main() 1, 3, 2, 2
ReqQueue.addExteriorCall() 1, 1, 2, 1
ReqQueue.addInteriorCall() 1, 1, 2, 1
ReqQueue.getExteriorCalls() 1, 1, 2, 0
ReqQueue.getInteriorCalls() 1, 1, 2, 0
--------------------------------------------------------------------------------------------
Block Depth Statements
0 10
1 35
2 44
3 35
4 21
5 18
6 17
7 3
8 0
9+ 0
--------------------------------------------------------------------------------------------
3.分析解读:
(1)基本信息:
行数:214
语句数:183
分支语句百分比:23.5%
方法调用语句数:79
带注释行的百分比:0.0%
代码规模有所增大,语句增多。无注释的情况对代码后续维护和理解极为不利。分支语句占比相对较高,代码中的逻辑分支较为复杂。
(2)类和方法信息:
类和接口数量:8 个,分别是Lift、ExternalCall、ReqQueue、ControlUnit、Passenger、MoveDirection(枚举)、State(枚举)、Main。
每个类的方法数:平均每个类有 3.00 个方法,类的功能相对分散,但ControlUnit类方法较多,可能存在职责不够清晰的问题。
每个方法的平均语句数:5.75 条,说明方法内部有一定的逻辑复杂度,并非简单的功能实现。
(3)复杂度分析:
1'最复杂方法
最复杂方法的行号:111
最复杂方法的名称:ControlUnit.operateLift()
最大复杂度:21
2'平均复杂度
平均复杂度:3.46
整体代码平均复杂度高于之前,说明多数方法的逻辑有一定复杂性,ControlUnit.operateLift()方法极大拉高了整体复杂度。
3'代码块深度分析
最深代码块的行号:139
最大代码块深度:7
平均代码块深度:2.87
最大代码块深度为 7,ControlUnit.operateLift()方法中存在多层嵌套的循环和条件判断,导致代码块深度较大。
我的思路与心得:
我的类图:

代码思路分析
整体架构搭建思路:
把电梯系统拆分成多个类,每个类负责特定功能。Main 类是程序入口,仅用于创建 ControlUnit 对象并依次调用其 handleInput 和 operateLift 方法,让程序启动和运行逻辑清晰。ControlUnit 类是核心,封装了电梯控制所需的属性(如 Lift 对象和 ReqQueue 对象)和行为(如处理输入请求、操作电梯运行),将电梯系统功能模块化,利于管理和维护。
Lift 类代表电梯,存储电梯当前楼层、运行方向、最高楼层和最低楼层等属性,还提供了上升、下降、设置方向等方法,抽象出电梯的基本行为。ExternalCall 类表示外部呼叫请求,包含请求楼层和请求方向。ReqQueue 类管理内部和外部请求队列,提供添加和获取请求的方法,将请求管理逻辑独立出来。
参数与属性定义思路:
在 Lift 类中,定义了 currentLevel 记录电梯当前所在楼层,dir 表示电梯运行方向,topFloor 和 baseFloor 确定电梯运行的楼层范围。这些属性能准确描述电梯的状态和运行范围,是电梯正常运行的基础。
ExternalCall 类包含 level 和 dir 两个属性,分别表示外部请求的楼层和方向,精确记录外部呼叫信息。
ReqQueue 类使用 LinkedList
ControlUnit 类包含 lift 和 requests 两个属性,分别表示电梯对象和请求队列对象,将电梯和请求管理关联起来,便于统一控制。
功能实现思路:
输入处理:ControlUnit 类的 handleInput 方法借助 Scanner 从控制台读取输入。若输入为纯数字,会设置电梯的最低和最高楼层;若输入以 < 开头并以 > 结尾,会解析请求信息,区分外部和内部请求并添加到请求队列中;若输入为 “end”,则停止输入处理。这种设计依据电梯系统实际使用场景,先确定系统运行的基本参数(楼层范围),再处理用户发出的请求指令。
方向调整:adjustDirection 方法依据当前楼层、当前方向、外部请求楼层、外部请求方向和内部请求楼层来调整电梯运行方向。它会分别考虑外部请求队列为空、内部请求队列为空以及两者都不为空的情况,根据楼层高低关系设置电梯运行方向,确保电梯能合理响应请求。
外部请求处理:processExterior 方法处理外部请求。当电梯到达外部请求楼层且方向一致或内部请求队列为空时,会打开和关闭电梯门,并从外部请求队列中移除该请求;若方向不一致,会根据具体情况调整电梯方向并处理请求。
内部请求处理:processInterior 方法处理内部请求。当电梯到达内部请求楼层且与外部请求楼层不同时,会打开和关闭电梯门,并从内部请求队列中移除该请求。
电梯运行:operateLift 方法是电梯运行的核心逻辑。它会不断循环,在每次循环中展示电梯当前楼层和运行方向,处理当前楼层的内、外部请求,若请求处理完毕则调整电梯运行方向并移动电梯,直到所有请求处理完成为止。这种设计模拟了电梯实际运行中 “停靠 - 处理请求 - 移动” 的流程。
我的实现:

心得总结
面向对象编程体会
本次实践加深了对面向对象编程的理解,将电梯系统拆分为多个类,封装数据与操作,使代码结构清晰、职责明确,提升了可读性和可维护性。但类和方法增多后,参数传递和逻辑协调变得复杂,后续设计需注重职责划分和参数传递的简洁性。
输入输出处理反思
使用 Scanner 读取输入简单直接,但输入校验不严格,仅对楼层范围有限检查,未充分处理异常输入,影响程序健壮性,后续应增加全面校验。输出仅简单打印信息,用户体验差,可采用图形界面或日志记录优化。
逻辑实现挑战与成长
实现电梯运行逻辑挑战大,判断运行方向和处理请求优先级需考虑多种情况。通过拆分问题、调试优化方法理清逻辑,处理边界情况要格外谨慎。这提升了分析和解决问题的能力,也让我明白处理复杂逻辑需全面测试确保正确性和稳定性。
题目集7与其代码展示:
点击查看题目集7题目
对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客类、队列类以及控制类,具体设计可参考如下类图。
类图
电梯运行规则与前阶段相同,但有如下变动情况:
乘客请求输入变动情况:外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)
注意:本次作业类设计必须符合如上要求(包含但不限于设计电梯类、乘客类、队列类以及控制类),凡是不符合类设计要求此题不得分,另外,PTA得分代码界定为第一次提交的最高分代码(因此千万不要把第一次及第二次电梯程序提交到本次题目中测试)。
输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<请求源楼层,请求目的楼层>,其中,请求源楼层表示乘客发起请求所在的楼层,请求目的楼层表示乘客想要到达的楼层。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:
运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
点击查看题目集7代码
import java.util.Scanner;
import java.util.LinkedList;
enum MoveDirection {
UP, STOP, DOWN
}
class Lift {
private int currentLevel = 1;
private MoveDirection dir = MoveDirection.UP;
private int topFloor;
private int baseFloor;
public Lift(int baseFloor, int topFloor) {
this.baseFloor = baseFloor;
this.topFloor = topFloor;
}
public void ascend() { currentLevel++; }
public void descend() { currentLevel--; }
public void setDirUp() { this.dir = MoveDirection.UP; }
public void setDirDown() { this.dir = MoveDirection.DOWN; }
public int getCurrentLevel() { return this.currentLevel; }
public MoveDirection getDirection() { return this.dir; }
public void setDirection(MoveDirection d) { this.dir = d; }
public int getTopFloor() { return this.topFloor; }
public int getBaseFloor() { return this.baseFloor; }
}
class ReqQueue {
private LinkedList<Passenger> interiorCalls = new LinkedList<>();
private LinkedList<Passenger> exteriorCalls = new LinkedList<>();
public void addInteriorCall(Passenger p) {
interiorCalls.add(p);
}
public void addExteriorCall(Passenger p) {
exteriorCalls.add(p);
}
public LinkedList<Passenger> getInteriorCalls() { return this.interiorCalls; }
public LinkedList<Passenger> getExteriorCalls() { return this.exteriorCalls; }
}
class ControlUnit{
private Lift lift;
private ReqQueue requests;
public ControlUnit() {
this.lift = new Lift(1, 10);
this.requests = new ReqQueue();
}
private void adjustDirection(int currLevel, MoveDirection currDir) {
LinkedList<Passenger> interior = requests.getInteriorCalls();
LinkedList<Passenger> exterior = requests.getExteriorCalls();
Integer extLevel = exterior.isEmpty() ? null : exterior.peek().getSourceFloor();
Integer intLevel = interior.isEmpty() ? null : interior.peek().getDestinationFloor();
if (exterior.isEmpty()) {
if (intLevel != null) {
if (currLevel > intLevel) lift.setDirDown();
else if (currLevel < intLevel) lift.setDirUp();
}
} else if (interior.isEmpty()) {
if (extLevel != null) {
if (currLevel > extLevel) lift.setDirDown();
else if (currLevel < extLevel) lift.setDirUp();
}
} else {
if (extLevel != null && intLevel != null) {
if (currLevel > extLevel && currLevel > intLevel) lift.setDirDown();
else if (currLevel < extLevel && currLevel < intLevel) lift.setDirUp();
}
}
}
private int processExterior(int currLevel, MoveDirection currDir) {
LinkedList<Passenger> exterior = requests.getExteriorCalls();
if (!exterior.isEmpty()) {
Passenger extPassenger = exterior.peek();
int extLevel = extPassenger.getSourceFloor();
MoveDirection extDir = extPassenger.getDirection();
if (currLevel == extLevel) {
if (currDir == extDir || requests.getInteriorCalls().isEmpty()) {
System.out.printf("Open Door # Floor %d\nClose Door\n", currLevel);
Passenger p = exterior.poll();
requests.addInteriorCall(new Passenger(p.getDestinationFloor()));
return 1;
} else {
if ((currDir == MoveDirection.UP && currLevel > requests.getInteriorCalls().peek().getDestinationFloor()) ||
(currDir == MoveDirection.DOWN && currLevel < requests.getInteriorCalls().peek().getDestinationFloor())) {
lift.setDirection(currDir == MoveDirection.UP ? MoveDirection.DOWN : MoveDirection.UP);
System.out.printf("Open Door # Floor %d\nClose Door\n", currLevel);
Passenger p = exterior.poll();
requests.addInteriorCall(new Passenger(p.getDestinationFloor()));
return 1;
}
}
}
}
return 0;
}
private int processInterior(int currLevel) {
LinkedList<Passenger> interior = requests.getInteriorCalls();
if (!interior.isEmpty()) {
Passenger intPassenger = interior.peek();
int intLevel = intPassenger.getDestinationFloor();
if (currLevel == intLevel) {
System.out.printf("Open Door # Floor %d\nClose Door\n", currLevel);
interior.poll();
return 1;
}
}
return 0;
}
public void operateLift() {
while (true) {
LinkedList<Passenger> interior = requests.getInteriorCalls();
LinkedList<Passenger> exterior = requests.getExteriorCalls();
int currLevel = lift.getCurrentLevel();
MoveDirection currDir = lift.getDirection();
System.out.printf("Current Floor: %d Direction: %s\n", currLevel, currDir.toString());
boolean handled = false;
int processed;
do {
processed = processExterior(currLevel, currDir);
if (processed == 0) processed = processInterior(currLevel);
handled = processed > 0;
} while (handled);
if (interior.isEmpty() && exterior.isEmpty()) break;
adjustDirection(currLevel, currDir);
MoveDirection newDir = lift.getDirection();
if (newDir == MoveDirection.UP) lift.ascend();
else if (newDir == MoveDirection.DOWN) lift.descend();
}
}
public void handleInput() {
Scanner inputScanner = new Scanner(System.in);
int base = 0, top = 0;
int inputCounter = 0;
while (inputScanner.hasNextLine()) {
String line = inputScanner.nextLine().trim();
if (line.equalsIgnoreCase("end")) break;
if (line.matches("\\d+")) {
if (inputCounter++ == 0) base = Integer.parseInt(line);
else {
top = Integer.parseInt(line);
lift = new Lift(base, top);
}
continue;
}
if (line.startsWith("<") && line.endsWith(">")) {
String content = line.substring(1, line.length() - 1);
String[] parts = content.split(",");
if (parts.length == 2) {
try {
int source = Integer.parseInt(parts[0].trim());
int dest = Integer.parseInt(parts[1].trim());
if (source >= base && source <= top && dest >= base && dest <= top) {
requests.addExteriorCall(new Passenger(source, dest));
}
} catch (NumberFormatException e) { /* Ignore invalid input */ }
} else {
try {
int dest = Integer.parseInt(content.trim());
if (dest >= base && dest <= top) {
requests.addInteriorCall(new Passenger(dest));
}
} catch (NumberFormatException e) { /* Ignore invalid input */ }
}
}
}
inputScanner.close();
}
}
class Passenger {
private Integer sourceFloor;
private Integer destinationFloor;
public Passenger(Integer sourceFloor, Integer destinationFloor) {
this.sourceFloor = sourceFloor;
this.destinationFloor = destinationFloor;
}
public Passenger(Integer destinationFloor) {
this.destinationFloor = destinationFloor;
}
public Integer getSourceFloor() { return sourceFloor; }
public Integer getDestinationFloor() { return destinationFloor; }
public MoveDirection getDirection() {
if (sourceFloor == null) return MoveDirection.STOP;
if (sourceFloor < destinationFloor) return MoveDirection.UP;
else if (sourceFloor > destinationFloor) return MoveDirection.DOWN;
else return MoveDirection.STOP;
}
}
public class Main {
public static void main(String[] args) {
ControlUnit control = new ControlUnit();
control.handleInput();
control.operateLift();
}
}
题目集7分析:
1.分析图例:

2.分析数据:
点击查看数据
Metrics Details For File 'Elevator3.java'
--------------------------------------------------------------------------------------------
Parameter Value
========= =====
Project Directory C:\JavaElevator\
Project Name SMTest
Checkpoint Name Baseline
File Name Elevator3.java
Lines 194
Statements 177
Percent Branch Statements 23.2
Method Call Statements 73
Percent Lines with Comments 1.0
Classes and Interfaces 6
Methods per Class 4.33
Average Statements per Method 5.12
Line Number of Most Complex Method 45
Name of Most Complex Method ControlUnit.handleInput()
Maximum Complexity 18
Line Number of Deepest Block 153
Maximum Block Depth 7
Average Block Depth 2.66
Average Complexity 3.19
--------------------------------------------------------------------------------------------
Most Complex Methods in 5 Class(es): Complexity, Statements, Max Depth, Calls
ControlUnit.adjustDirection() 18, 22, 4, 12
ControlUnit.ControlUnit() 1, 2, 2, 0
ControlUnit.handleInput() 18, 29, 7, 16
ControlUnit.operateLift() 8, 21, 4, 13
ControlUnit.processExterior() 11, 19, 6, 20
ControlUnit.processInterior() 3, 9, 4, 6
Lift.ascend() 1, 1, 2, 0
Lift.descend() 1, 1, 2, 0
Lift.getBaseFloor() 1, 1, 2, 0
Lift.getCurrentLevel() 1, 1, 2, 0
Lift.getDirection() 1, 1, 2, 0
Lift.getTopFloor() 1, 1, 2, 0
Lift.Lift() 1, 2, 2, 0
Lift.setDirDown() 1, 1, 2, 0
Lift.setDirection() 1, 1, 2, 0
Lift.setDirUp() 1, 1, 2, 0
Main.main() 1, 3, 2, 2
Passenger.getDestinationFloor() 1, 1, 2, 0
Passenger.getDirection() 5, 8, 2, 0
Passenger.getSourceFloor() 1, 1, 2, 0
Passenger.Passenger() 1, 1, 2, 0
Passenger.Passenger() 1, 2, 2, 0
ReqQueue.addExteriorCall() 1, 1, 2, 1
ReqQueue.addInteriorCall() 1, 1, 2, 1
ReqQueue.getExteriorCalls() 1, 1, 2, 0
ReqQueue.getInteriorCalls() 1, 1, 2, 0
--------------------------------------------------------------------------------------------
Block Depth Statements
0 8
1 36
2 52
3 30
4 28
5 11
6 10
7 2
8 0
9+ 0
--------------------------------------------------------------------------------------------
3.分析解读:
(1)基本信息:
行数:194
语句数:177
分支语句百分比:23.2%
方法调用语句数:73
带注释行的百分比:1.0%
分析:代码规模较题目集 6 略有减小,但分支逻辑复杂度相近。尽管新增 1% 注释,仍缺乏关键逻辑说明,影响维护。分支语句占比高,反映电梯调度逻辑(如方向调整、请求处理)仍较复杂。
(2)类和方法信息:
类和接口数量:6 个(Lift、Passenger、ReqQueue、ControlUnit、MoveDirection 枚举、Main)。
每个类的方法数:平均 4.33 个,ControlUnit 类承担核心调度逻辑,方法数最多(6 个),职责集中但未明显违反单一职责原则。
每个方法的平均语句数:5.12 条,方法逻辑中等复杂度,核心方法(如handleInput)包含输入解析与校验,语句较多。
(3)复杂度分析:
1' 最复杂方法:
行号:45
名称:ControlUnit.handleInput()
最大复杂度:18
分析:该方法负责输入解析、楼层范围设置、请求类型判断及合法性校验,包含多层条件分支(如判断输入格式、区分内外部请求、过滤无效楼层),导致复杂度较高。
2' 平均复杂度:3.19
分析:整体复杂度低于题目集 6,核心方法adjustDirection和processExterior复杂度为 18 和 11,主要因请求队列转换(外部请求转内部请求)引入额外逻辑,但模块化设计使部分方法复杂度降低(如operateLift复杂度仅 8)。
3' 代码块深度分析:
最深代码块行号:153
最大代码块深度:7
分析:深度出现在processExterior方法的方向匹配逻辑中(多层if-else嵌套),但平均块深度 2.66 较题目集 6 有所下降,代码结构更扁平。
我的思路与心得:
我的类图:

代码思路分析
整体架构搭建思路:
类职责划分:
Lift 类:封装电梯状态(当前楼层、方向、楼层范围)和基础操作(升降、方向设置),符合单一职责。
Passenger 类:独立管理乘客请求数据(源楼层、目的楼层、方向计算),外部请求处理后自动转为内部请求,解耦请求生命周期。
ReqQueue 类:统一管理内外部请求队列,通过LinkedList实现先进先出,支持请求添加与获取。
ControlUnit 类:核心控制逻辑,处理输入解析、方向调整、请求执行,协调各组件交互。
模块化优势:
乘客类将请求数据与业务逻辑(如方向计算)封装,避免控制类直接处理原始数据,提升可维护性。
队列类统一管理请求生命周期(外部请求出队时自动添加内部请求),符合开闭原则。
功能实现思路:
输入处理(handleInput):
解析前两行输入为楼层范围,创建 Lift 实例。
后续行解析为内 / 外部请求:
内部请求:<楼层数>,直接创建 Passenger(仅目的楼层)加入内部队列。
外部请求:<源楼层,目的楼层>,校验楼层范围后创建 Passenger(含源和目的楼层)加入外部队列。
异常处理:通过try-catch过滤非数字输入,提升鲁棒性。
方向调整(adjustDirection):
根据内外部请求队列头部元素的楼层,结合当前楼层与方向,动态调整电梯运行方向:
若外部队列为空,根据内部请求楼层调整方向。
若内部队列为空,根据外部请求楼层调整方向。
若两者均非空,按当前楼层与请求楼层的高低关系决定上行或下行。
请求处理:
外部请求(processExterior):
到达源楼层且方向匹配时,开门并将目的楼层转为内部请求(requests.addInteriorCall)。
方向不匹配时,若当前楼层超过内部请求最高层或低于最低层,调整方向后开门。
内部请求(processInterior):到达目的楼层时开门,请求出队。
电梯运行(operateLift):
循环处理请求:先处理外部请求,再处理内部请求,每次处理后检查队列是否为空。
无请求时结束循环,否则根据adjustDirection结果移动电梯(上升 / 下降)。
我的实现:

心得总结
面向对象编程体会
本次迭代通过引入乘客类(Passenger)重构请求逻辑,进一步践行单一职责原则。将乘客请求数据(源楼层、目的楼层、方向)封装至独立类,避免控制类直接处理原始参数,使代码结构更清晰。例如,Passenger.getDirection() 方法将方向计算逻辑内聚,控制类仅需调用接口,降低了模块间耦合度。但类间协作复杂度增加(如外部请求转内部请求时的队列操作),需更严格规划参数传递路径,避免因对象状态不一致导致逻辑混乱。
输入输出处理反思
输入解析通过正则匹配和try-catch过滤部分无效数据(如非数字输入),但仍存在校验盲区:
未处理外部请求中源楼层等于目的楼层的无效场景(如<5,5>),可能导致无意义的请求入队。
对重复请求的去重逻辑缺失(如连续输入相同外部请求),可能影响队列效率。
输出层面延续文本打印方式,虽满足基本功能,但缺乏可视化反馈(如电梯运行轨迹动画),用户体验优化空间较大,可考虑引入日志框架记录详细运行数据。
逻辑实现挑战与成长
核心难点在于外部请求与内部请求的管理:
请求转换逻辑:外部请求处理后将目的楼层转为内部请求,确保队列操作的原子性(如先出队外部请求,再入队内部请求),调试中因顺序颠倒导致请求丢失。
方向调整边界:当电梯满载且内外请求方向冲突时(如外部请求上行,内部请求均下行),优先处理同方向请求。过程中深刻体会到边界测试的重要性(如楼层等于基底层 / 顶层时的转向逻辑),需通过等价类划分法设计测试用例,确保极端情况处理正确。
总结
对于本人而言,此次工程作业,压力是比较大的,耗费了很多时间。尤其是第一次对题目描述的理解很模糊,以至于将代码重写了两次。在这个过程中,深刻体会到准确理解需求的重要性 —— 初期因未明确 “外部请求转内部请求” 的逻辑,导致类设计偏离要求,不得不推翻原有架构。
重写过程中,通过反复研读题目要求,逐步梳理出 “输入解析→请求建模→队列管理→电梯调度” 的清晰流程。在处理外部请求时,明确了 “源楼层验证→目的楼层提取→内部请求入队” 的步骤。同时,为了避免重复代码,将楼层范围校验逻辑抽取为公共方法,减少了handleInput方法的复杂度。
尽管多次重写增加了时间成本,但也带来了意外收获:通过对比不同版本的代码结构,更直观地理解了面向对象设计中 “高内聚、低耦合” 的实践方式。例如,第3次迭代中ReqQueue类专注于队列操作,ControlUnit仅负责调度逻辑,而Passenger类独立管理请求数据,这种解耦设计使得后续功能扩展(如增加乘客优先级)变得更为容易。
此外,在调试过程中,通过分析复杂度数据(如ControlUnit.adjustDirection复杂度 18),意识到部分方法存在逻辑过度集中的问题。为此,尝试将方向调整逻辑拆分为 “外部请求优先” 和 “内部请求优先” 两个子方法,虽然未能完全降低复杂度,但代码的可读性得到了明显提升。这也提醒自己,在未来的设计中应更早引入模块化思维,避免单一方法承担过多职责。
总的来说,此次作业的曲折经历不仅是对技术能力的考验,更是对问题分析和解决流程的一次全面复盘。尽管过程艰辛,但通过不断试错和重构,最终实现了代码结构的优化和功能的正确实现,也为后续类似项目积累了宝贵的经验 —— 准确的需求分析是基石,合理的架构设计是关键,而持续的代码重构则是提升质量的必要手段。
浙公网安备 33010602011771号