第一次迭代电梯博客作业

引言

作为第一次带迭代式作业,对初庐未久的我来说着实是一项较高挑战,本次题目集作业讲究,功能不断完善,要求层层递增的章法,从最基本的运行逻辑到逐渐添加的输入验证和类的职责单一化,但核心的电梯运行逻辑基本不变。
~~ ~~

设计与分析

  • 第一次实验迭代作业

设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。
请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求),具体运行规则详见输入输出样例。

输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。

电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用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
类图


image


SourceMonitor分析


image
image

1 ​​Direction枚举​​:
包含UP和DOWN两个方向常量
提供opposite()方法用于方向反转
2 ​​Request类​​:
核心属性:
isExternal:区分内外请求
floor:目标楼层
dir:请求方向(仅外部请求有效)
构造方法:初始化请求参数
3 ​​ElevatorSystem类​​:
主要成员变量:
两个LinkedList分别存储内外请求
ArrayList用于记录输出日志
Scanner用于输入处理
minFloor/maxFloor记录电梯范围
main方法:包含整个调度逻辑
4 ​​关联关系​​:
组合关系:电梯系统包含多个请求对象
依赖关系:Request类使用Direction枚举

  • 代码分析
    首先
    1 方法体平均长度22语句,main方法明显过长,存在深度为5的嵌套。
    接着在类的设计也使有缺陷的,
    2 ElevatorSystem违反了单一原则的问题:
    ElevatorSystem承担了调度器、移动控制、IO处理等多重职责。
    3 ​​状态管理缺失​​:电梯运行状态(如门开关)分散在字符串操作中。
    4 ​​注释率6.6%​​:低于行业标准(建议≥20%),关键算法缺乏文档说明
点击查看雷达图
代码健康度雷达图(满分5分):
┌───────────┐
│ 可维护性 3 │
│ 可读性   2 │ 注释覆盖率 1
│ 扩展性   2 │ 方法复杂度 4
└───────────┘

心路历程

由于本人第一次迭代的作业是没有过的,这里只要分析一下遇到问题的心路历程和后续改进的建议。
先是第一次看到如此复杂且难解题意的题目,是很头大的,刚开始拿到这个题目不知道从什么地方下手去做,然后就厌题了,不想做,后来也就做出水平很差的代码。

建议与改进

  • 遵循单一职责原则:一个类应该只有一个引起它变化的原因。像在电梯系统里,ElevatorSystem类承担了过多职责,导致代码维护困难。新手在开发时,要把不同功能拆分成独立的类,让每个类只专注于一项任务,这样代码的可维护性和可扩展性都会提高。比如可以把输入处理、调度逻辑、电梯控制等功能分别封装成不同的类。

  • 运用抽象思维建模:在开始编码前,先识别出关键的实体和行为,利用枚举、接口或者抽象类来定义好规则。例如在电梯系统中,提前定义好Direction枚举和ElevatorState枚举,能让代码逻辑更清晰,减少后期重构的可能性。

  • 第二次实验迭代作业

对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(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
输入样例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
类图


image

SourceMonitor分析


image
image

面临问题

整个代码的复杂度还是超出了正常的范围,类的设计及功能的管理还是有很大的空间,对于老师在第二次给的提示中仍然没有做到最终的要求。

代码分析

类职责失衡(6个类但功能集中)

  • ElevatorControllerInfo承担了:
    请求调度(策略模式职责)
    状态管理(状态模式职责)
    移动控制(命令模式职责)
  • 方法复杂度超标(Max Complexity=8.2)
    processUpwardRequests()和processDownwardRequests()方法:
点击查看代码
java
if (A && B || C) {  // 4层嵌套逻辑
    if (D) {        // 5层
        while(E) {  // 6层
            switch(F) { // 7层

影响:代码覆盖率难以提升(预计单元测试成本增加50%)

  • 同样的注释不足的情况,阅读和查找难度更大。

踩坑心得和体会

  • 相较于第一次的代码来看,总体上类的架构设计还是更进了一步的,职责更较为完善了一些,在老师提醒的是look的算法的时候,仔细研究下,本题的逻辑还是有出入的,本题只对队列的前两个的请求做出分析判断和回应,是look的变式版。
    (LOOK算法是SCAN(电梯扫描)算法的优化变种,其核心行为模式为:
    plaintext
  1. 保持当前方向移动,直到该方向无更多请求
  2. 立即调头(不移动到物理终点)
  3. 重复上述过程)

-然后本人不听劝,用了linklist和选择下表遍历的方式,并讲所有的逻辑的列举处理的方式是的代码的复杂度和长度都不符合电梯的题目要求,导致连测试都放不上去,最终遗憾告终。
这种不符合计算机的编程方式还是不值得去尝试,较长的迭代逻辑通常嵌套较深,难以阅读和维护,容易引入隐蔽的边界条件错误。

  • 缓解策略
    分块处理:将大任务拆分为小批次(如分页查询、批量处理)。
    优化算法:降低时间复杂度(如用哈希表替代嵌套循环)。

  • 第三次实验迭代作业

对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客类、队列类以及控制类,具体设计可参考如下类图。

电梯运行规则与前阶段相同,但有如下变动情况:

乘客请求输入变动情况:外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)
注意:本次作业类设计必须符合如上要求(包含但不限于设计电梯类、乘客类、队列类以及控制类),凡是不符合类设计要求此题不得分,另外,PTA得分代码界定为第一次提交的最高分代码(因此千万不要把第一次及第二次电梯程序提交到本次题目中测试)。

输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。

电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<请求源楼层,请求目的楼层>,其中,请求源楼层表示乘客发起请求所在的楼层,请求目的楼层表示乘客想要到达的楼层。
当输入“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

image

  • 分析和设计
    本次更改主要针对的是在输入方面的问题和少部分电梯的运行逻辑:讲内部队列的请求移至外部队列的后面,但总的判断电梯的运行的算法没有较大的变化。

  • 面临困境
    image


image
image

在想这个测试点的时候一直有疑惑然后改了很多也没有改到点子上,先是电梯的初始楼层的问题,当我的电梯没有从第一层开始运行时候我的电梯是从minfloor开始判断的,导致的问题就是,电梯从一层直接到第一个初始请求了,从调整了电梯的初始楼层属性后发现可以了但还是没有改到测试点上。

  • Main 类:
    该类包含 main 方法,作为程序的入口点。
    负责读取用户输入,直至遇到 "end" 标志,之后解析出楼层范围(minFloor 和 maxFloor)。
    创建 RequestQueue、Elevator 和 ElevatorController 对象,并且调用ElevatorController 的 run 方法来启动电梯调度逻辑。
  • Direction 枚举:
    定义了两个方向常量 UP 和 DOWN,用于表示电梯的运行方向。
  • RequestQueue 类:
    该类管理两个队列,分别是内部请求队列 internalRequest(使用 ArrayDeque 存储)和外部请求队列 externalRequest(使用 LinkedList 存储)。
    提供了添加内部请求和外部请求的方法 addInternalRequest 和 addExternalRequest,还提供了获取这两个队列的方法。
  • Elevator 类:
    代表电梯实体,包含当前楼层、运行方向、最大楼层和最小楼层等属性。
    提供了获取和设置这些属性的方法,以及模拟开门关门的 openDoor 方法。
  • ExternalRequest 类:
    表示外部请求,包含请求楼层、目标楼层和请求方向等属性。
    重写了 equals 和 hashCode 方法,用于判断两个外部请求是否相等。
  • ElevatorController 类:
    核心调度类,负责控制电梯的运行。
    run 方法是主循环,只要请求队列不为空,就会持续处理请求。
    handleUpwardRequests 和 handleDownwardRequests 方法分别处理电梯向上和向下运行时的请求。
    moveElevator 方法实现电梯的移动逻辑,包括逐层移动到目标楼层,以及处理到达目标楼层时的开门关门操作。

不足与改进建议

  • 方法复杂度:
    handleUpwardRequests 和 handleDownwardRequests 方法嵌套层次较深,逻辑复杂,可读性和可维护性较差。
    建议将部分逻辑提取成独立的方法,例如将判断下一个请求目标的逻辑封装成单独的方法,以减少嵌套层次。
  • 单一职责原则:
    ElevatorController 类承担了过多的职责,包括请求处理、电梯移动控制和开门关门操作等。
    可以进一步拆分功能,例如将请求处理逻辑封装到单独的类中,使每个类的职责更加单一。
  • 状态管理:
    电梯的运行状态(如开门、关门、移动等)没有显式的状态管理,不利于后续扩展和维护。
    可以引入状态模式,定义不同的电梯状态类,每个状态类负责处理相应的行为。
  • 注释和文档:
    代码中的注释较少,尤其是关键逻辑部分,不利于其他开发者理解代码。
    建议添加详细的注释,特别是在复杂的逻辑和算法部分,同时可以为类和方法添加 Javadoc 注释

总结

收获

  • 正则表达式
    本次题目集最让我印象深刻的就是他了,合理的运用可以使得输入变得无比强大,下面题几个遇到的问题
  1. 转义字符使用错误
    误区:在 Java 中,正则表达式的反斜杠\需要双重转义(因为 Java 字符串本身需要转义)。
    错误示例:"\d+" 是正确的,而"\d+" 会报错(Java 字符串中\d无意义)。
    场景复现:代码中匹配尖括号时必须写成"<\d+>"(第一个\转义尖括号,第二个\是 Java 字符串的转义符)。

  2. 忽略边界匹配
    误区:未使用^(开头)和$(结尾)限制匹配范围,导致误匹配。
    错误示例:验证内部请求时若用"\d+",则字符串"a123b"中的123也会被匹配,而正确写法应为"^<\d+>$"。

  3. 枚举类型的实际应用
    定义Direction枚举表示电梯运行方向(UP/DOWN),替代传统的字符串或整数常量,使代码更具语义化和类型安全性。
    在计算请求方向时(targetFloor > sourceFloor ? Direction.UP : Direction.DOWN),直接使用枚举值判断,避免魔法值,提升逻辑清晰度。

  4. 逻辑调试与边界测试
    在逐层移动电梯的循环中(while (currentFloor != targetFloor)),通过打印当前楼层和方向(System.out.println),便于跟踪电梯运行路径,定位调度逻辑中的错误。
    测试边界情况:
    当所有请求均在当前楼层下方时,电梯应切换为向下运行。
    外部请求转为内部请求时(requestQueue.addInternalRequest(extReq.getTargetFloor())),确保目标楼层被正确加入内部队列。

改进和建议

通过完成这个电梯调度系统,使得我进一步熟悉了 Java 基础语法(枚举、集合、正则表达式)和面向对象设计的基本思想:
问题建模能力:将现实中的电梯运行逻辑抽象为代码中的类、属性和方法。
逻辑推导能力:设计符合实际场景的调度规则(如方向优先、请求排序)。
调试与优化思维:通过测试边界条件和简化代码结构,逐步提升程序的健壮性和可维护性。
同时有利于用户面临更加复杂的问题最初更好的解决办法好方案,增强自己独立思考和解决复杂现实问题的能力。

  • 对题目集的建议
    分值设置的不太合理,分数对测试点的分配以及相关监测点,我觉得可以采用循序渐进的方式,不然容易产生挫败的心理,确实比较折磨,对于刚入场没多久的小白打击还是有点大的。
  • 对于题目集的文字性描述方面还可做的感觉更好吧,可已将问题讲的更清晰,更详细一点,不然会多做后多无用功,导致额外的浪费吧。
posted @ 2025-04-20 13:52  断川鸿  阅读(48)  评论(0)    收藏  举报