一.前言
5~7题目集题量不算多,每次都三四道题就开始是大作业了,开始都是比较基础中等的题目,大部分和C语言题目是一样的,解题思路都差不多,只是需要用到Java的知识点解答。但是对我来说三次电梯题还是很难的,需要用到很多Java的知识:第五次题目集的电梯题涉及到的类不多,但是类里面定义了很多方法,需要用到队列调度算法(不太会),使用LinkedList、ArrayList来存储请求,用到for-each增强循环;第六次电梯题给出了所需要使用到的类图,对第五次电梯进行迭代性训练,类的设计须遵循单一职责原则,我也是第一次接触到枚举类enum、Collection 接口的remove方法等等;第七次题目集加入乘客类(Passenger),需要取消乘客请求类,同时类设计要求遵循单一职责原则(SRP),两次迭代练习对我来说难度较大,解答起来也很困难。
二.设计与分析
1.第五次电梯题:
- 题目: 设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。
请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求),具体运行规则详见输入输出样例。
输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:
运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
- 题目分析:
第五次电梯题不需要考虑很多的类间关系,更注重算法设计(LOOK算法为核心)来设计电梯运行逻辑,电梯代码设计要对同时考虑内部和外部的请求,遵循同方向优先、头部请求先处理的原则设计代码。但是我对电梯LOOK算法并不清楚熟悉,也不会用,所以没完整考虑电梯运行的逻辑,没写出来。
类图:

这张类图设计了Elevator、Direction、enum和Main类。Elevator 类依赖 Direction 枚举类来定义电梯运行方向相关逻辑;Main 类依赖 Elevator 类来创建电梯对象并
处理电梯相关操作,同时依赖 Direction 枚举类来处理方向相关信息。这种依赖关系体现了不同类在功能实现上的协作,一个类的部分功能实现需要借助另一个类的定义或对
象。
SourceMontor生成报表:

代码分析可以知道我的代码结构还是很清晰的,通过Elevator类承担电梯功能运转的实现、Direction类对电梯请求和电梯运行方向的定义变化、Main类的程序入口及输入职责等等;我的Elevator类封装了电梯运行的相关属性和方法将电梯的状态管理、请求处理等逻辑集中在一起,便于理解和维护都体现了Java的封装性和单一职责原则。但是从分析报表来看我的代码复杂性比较高,从度量信息可知,部分方法如 Elevator.getNextFloor() 和 Main.main() 复杂度达到 9 ,平均复杂度也有 4.25 。较高的复杂度意味着代码逻辑可能较为混乱,难以理解和调试。可能存在方法功能过于复杂、嵌套过多等问题。同时,我的代码缺少注释、注释百分比较低,不利于后续再次查看代码是快速理解代码的意图、功能以及各方法之间的调用关系等,不利于代码的维护和协作开发。总体来看我的代码结构还不完善,无法应对多变的需求,当后续如要使用更复杂的调度算法,则可能需要对当前代码进行较大的改动,同时我的代码对于一些异常情况,如如无效楼层请求的更详细处理、输入格式错误时的健壮性处理等可能会导致程序在面对异常输入时可能出现错误或不稳定的情况。总的来说,代码逻辑、细节上存在着很多问题,质量并不高。
2.第六次电梯迭代训练
- 题目:对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(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
- 题目分析
这次题目在第五次的基础上新增加了几点要求乘客请求楼层数有误,具体为高于最高楼层数或低于最低楼层数,处理方法:程序自动忽略此类输入,继续执行;乘客请求不合理,具体为输入时出现连续的相同请求,例如<3><3><3>或者<5,DOWN><5,DOWN>,处理方法:程序自动忽略相同的多余输入,继续执行,例如<3><3><3>过滤为<3>。
类图:

根据题目上的类图分析可知Elevator 类与 Direction、State 枚举类:Elevator 类通过属性 direction 和 state 依赖 Direction 和 State 枚举类,用于定义电梯的运行方向和状态,这是一种简单的类型依赖,使 Elevator 类能使用枚举类型来规范自身属性的取值范围。ExternalRequest 类与 Direction 枚举类:ExternalRequest 类的 direction 属性依赖 Direction 枚举类,用于表示外部请求的方向,确保请求方向的表示符合系统定义的标准。Controller 类与 Elevator 类、RequestQueue 类:Controller 类通过持有 Elevator 类和 RequestQueue 类的对象引用,依赖这两个类来实现电梯请求处理和运行控制的逻辑。例如,Controller 类需要调用 Elevator 类的方法来操作电梯,通过 RequestQueue 类来管理和获取乘客请求。RequestQueue 类与 ExternalRequest 类:RequestQueue 类的 externalRequests 属性是 LinkedList
SourceMontor生成报表:

分析可知,我的代码功能模块划分较清晰,通过 Direction 和 State 枚举类来定义电梯的运行方向和状态。但是从度量报告可知,平均复杂度为 9.00 ,且 Main.main() 方法复杂度达到 9 ,行数较多(289 行 ) 。较高的复杂度意味着代码逻辑可能较为混乱,难以理解和调试。可能存在方法功能过于复杂、嵌套过多、代码重复等问题,导致代码的可维护性降低。含注释的代码行占比仅 3.5% ,缺乏注释。同时某些类的方法数量较多(平均每个类的方法数为 16.50 ),可能存在类的职责不够单一的情况,将过多功能集中在一个类中,导致类的功能边界不够清晰,增加了理解和维护的难度。
- 运行代码:
输入
1
20
< 3,UP>
<5>
<6,DOWN>
<7>
<3>
end
输出
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 3 Direction: UP
Current Floor: 4 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 5 Direction: UP
Open Door # Floor 6
Close Door
Current Floor: 6 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 7 Direction: UP
Current Floor: 8 Direction: UP
可知未输出需停靠电梯的3层楼,且后面电梯运行也未准确输出。
3.第七次电梯迭代
- 题目:电梯运行规则与前阶段相同,但有如下变动情况:
乘客请求输入变动情况:外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)
注意:本次作业类设计必须符合如上要求(包含但不限于设计电梯类、乘客类、队列类以及控制类),凡是不符合类设计要求此题不得分,另外,PTA得分代码界定为第一次提交的最高分代码(因此千万不要把第一次及第二次电梯程序提交到本次题目中测试)。
输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<请求源楼层,请求目的楼层>,其中,请求源楼层表示乘客发起请求所在的楼层,请求目的楼层表示乘客想要到达的楼层。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:
运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
- 题目分析
对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类。
类图:

类图中,新增Passenger 类,属性有 sourceFloor(请求源楼层,类型为 Integer )和 destinationFloor(请求目的楼层,类型为 Integer ) ,用于存储乘客的请求信息。
方法:提供了多个构造函数,方便根据不同情况创建乘客对象;以及一系列获取和设置楼层属性的方法(如 getSourceFloor、setDestinationFloor 等 ),用于操作乘客请求的楼层信息。同时RequestQueue 类的 internalRequests 和 externalRequests 属性是 LinkedList
SourceMontor的生成报表:

报表内容可知Main.main() 方法作为最复杂的方法,行数达 190 ,复杂度为 4 。这表明程序入口的 main 方法逻辑较为复杂,可能存在功能过于集中、代码结构混乱等问题;平均每个类的方法数为 18.50 ,类承担的方法数量较多。这可能导致类的职责不够清晰,违反单一职责原则;并且分支语句占比 29.5% ,说明代码中条件判断逻辑较多。总的来说,代码质量并不是很高,还违反了单一职责原则。
三.踩坑心得
1.注重编程细节,结合类图给出代码:
多次调试显示编译错误,是因为犯了很多常见的错误,比如instance未正确赋值,未正确初始化等等,开始我的电梯代码思路并不是很清晰,在第五次的电梯题里没有分很多的类而是在Elevator类有很多的方法去实现电梯运行操作,所以刚开始并不能完整的解答题目的方法思考出来,而是一步步涉及到一步步添加进去,也没有通过画类图鲜明地把所需用到的方法清楚的罗列出来,所以到后面也是越来越乱,所以到第五次题目集关闭的时候都不够时间去重新理思路和修改。
2.厘清解答问题思路:
到了第六次电梯迭代训练的时候,结合第五次的思路和题目中给出的类图重新思考,就能够给出比较完全的代码,但是多次运行都显示运行超时

经查询可能是算法效率低、死循环或无限递归或者是多线程原因,我也一直在修改调试,直到6关闭也没找出问题来。
3.分清类设计与职责划分,正确分配任务:
我的代码电梯类既负责电梯状态管理,又混杂请求处理等逻辑,类承担过多功能,违反单一职责原则;同时电梯运行规则处理不当:对电梯运行规则(如优先处理同方向请求、顺路请求处理等 )实现有误。比如没有正确判断和处理电梯在不同状态下(上行、下行、空闲 )对请求的响应顺序,导致运行结果与预期不符。

四.改进建议
结合三次电梯题的SourceMontor的生成报表内容总结出一下改进点:
1.增加注释:
从报表可知注释占比极低(如含注释代码行占比仅 2.5% ),在关键方法、复杂逻辑处添加注释,解释方法功能、算法思路、参数意义等。
2.拆分复杂方法:
可将复杂方法中不同功能逻辑拆分成多个小方法,使每个方法功能单一、逻辑简洁。
3.优化类的职责:
报表显示部分类方法数量较多(平均每个类方法数达 18.50 ),可能存在职责不单一问题。我需要遵循单一职责原则,重新审视类的功能,将一个类中不同类型功能拆分到不同类中。比如电梯类若同时处理电梯状态和请求队列,可将请求队列管理功能分离到独立的请求队列类中。
4.简化分支逻辑:
报表显示分支语句占比较高(如达 29.5% ),会使代码逻辑复杂难理解。我的电梯解答多用 if - else、switch 等分支语句,因此我需要检查这些语句看是否能合并条件、优化判断逻辑。比如在判断电梯运行方向和停靠楼层时,若存在多层嵌套分支,尝试用更简洁的算法或数据结构替代。
5.简化代码:
我的代码逻辑不够缜密,多处统一功能在多处重复定义实现的问题,则需简化代码,使其逻辑更清晰,代码无冗余。
五.总结
1.收获
在三次电梯题训练中,我更加深刻的认识到了类与对象是定义多个类来模拟电梯系统的不同组件,如 Elevator 类代表电梯,封装了电梯的属性(当前楼层、运行方向、状态等)和方法(移动到指定楼层、判断楼层有效性等);Passenger 类代表乘客,包含乘客的请求信息(源楼层、目的楼层)。通过创建这些类的对象,将电梯系统的各个部分实例化,使其能够独立工作并相互协作,实现整个电梯调度功能;还有Java的封装性(学习对类的属性进行封装),并且提供公共的 getter 和 setter 方法来访问和修改这些属性;同时在处理乘客请求时,使用 ArrayList 或 LinkedList 来存储乘客请求,学习到了ArrayList 基于动态数组实现,适合随机访问元素,对于需要频繁查找请求的场景较为合适;LinkedList 基于双向链表实现,在插入和删除元素时效率较高,适合频繁增删请求的场景;并且新学习到了使用 Queue 接口及其实现类(如 LinkedList 可作为队列使用)来管理乘客请求队列;学习到了在获取一长字符串时用.nextLine()输入;学习到了使用 enum 关键字定义枚举类型。
2.自我改进
在学习到了很多知识以外,我也很清楚地认识到了我现在对Java课程的部分还需深层次的学习。我对输入输出的处理不熟练,对 Scanner 类的使用不够灵活,尤其是在处理不同格式的输入(如包含特殊字符的楼层输入)时,没有进行有效的输入验证和异常处理,导致出现 NumberFormatException 等异常;在这几次代码中我可能对泛型的概念理解不够深入,没有充分认识到泛型在提高代码复用性和类型安全性方面的重要作用。并且在实际代码中,没有合理运用泛型来定义类、接口或方法;对于6、7题目集的电梯题,涉及到了很多类,在调试过程中我也知道了自己对类的设计和职责划分不合理,在设计类时,没有遵循单一职责原则,导致类的职责过于复杂,一个类承担了过多的功能,类之间的关系设计不够清晰,存在依赖关系混乱、耦合度高等问题。对于类与类之间的关系,我对继承和多态的应用不够灵活,对继承和多态的概念理解不够深入,在写电梯代码中,可能没有合理运用继承和多态来设计类和方法,导致代码有些混乱。
总之,在今后我会更加注重基础知识的学习,深入学习单一职责原则,通过绘画类图来明确职责,同时在编写代码时养成添加注释的习惯,多做算法练习题、提高算法设计和实现能力。
浙公网安备 33010602011771号