南昌航空大学JAVA大作业分析Blog1

一.前言
本次博客针对第五至七次JAVA大作业进行分析与总结,在这三次大作业中,个人认为除了迭代式大作业题目(单部电梯调度系统)其他的题目难度并不算高,主要在于一些题目已经给出了参考主函数,只需按题目要求与主函数所缺的类与方法进行补充即可,阅读完主函数的处理逻辑,对于本次题目的类设计与方法逻辑就已经有了大致的思路,后续的设计与编程也变得轻松很多。某些题目的设计及其巧妙,光阅读完题目就感到了一种焕然一新的感觉,尤其在于题目集七中的《蒙特卡罗方法求圆周率》,不仅以文档的形式来给出蒙特卡罗方法求圆周率的详细介绍,同时在编程题中通过概率方法来求解题目的设计在之前也并未见过,即增强了我们学生对于新事物的学习能力,又考验了我们编程能力的综合素养。对于本轮大作业的重中之重———《单部电梯调度系统》,难度确实不小,难点重点在于对题目电梯运行逻辑的分析与判断,题目中的电梯运行逻辑与我们正常生活中逻辑有所不同,需要我们学生通过结合题目与测试样例来进行自己的探索,因此最难的一次题目个人认为是第五次大作业中的电梯调度系统,因为初见该系统,需要花费大量时间进行探索与试验,并且对于边界条件时的调试也需要进行分析和试错,总体而言难度不低。综上,个人认为本轮题目主要考察我们学生的类设计能力和独自分析与解决问题的能力,考察点较为综合,重点在于编程思维的培养与编程实现的能力。
二.设计与分析
第五次题目集:
7-5 NCHU_单部电梯调度程序
分数 70
中等
作者 段喜龙
单位 南昌航空大学
设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为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
在第五次题目集中前四道题目均考察正则表达式,难度不高,只要熟悉正则表达式的使用就很容易得分。而第五道题目为电梯调度系统的初见,个人因为操作失误导致该题未得分,但已经有了解题思路,电梯的运行仅看外部与内部队列的队头,并不关注后续的队列请求,只要把握这一点,对于解决这个问题就已经成功一半了。
因为由于操作失误未能合理提交答案,因此没有办法进行代码分析。

第六次题目集:
7-3 NCHU_单部电梯调度程序(类设计)
分数 50
较难
作者 段喜龙
单位 南昌航空大学
对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(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
在第六次题目集中电梯调度系统最大的改动在于详细明确了类设计要求,至于输入的更改,影响感觉并不是特别大,并没有重构类设计这一点复杂。
代码分析如下:
通过分析软件的分析可以得出如下数据:

总结如下:

<1>代码规模​​
代码行数:427
有效语句数:265
类/接口数:7
<2>​​复杂度指标​​
最高复杂度方法:Controller.getNextFloor()(第248行),复杂度19
最深代码块深度:7(第217行)
平均块深度:2.42
​​<3>代码质量​​
注释行占比:0.0%(无注释)
分支语句占比:22.3%
方法调用语句:134
类平均方法数:5.29
方法平均语句数:5.49
<4>雷达图
注释占比(% Comments)为0,雷达图在此维度完全凹陷
最大复杂度(Max Complexity)和最大块深度(Max Depth)处于外缘,显示极端值
方法/类(Methods/Class)接近中间值,类规模较均衡
深度0-2的浅层代码块占比最高(约80%)
深度≥5的深层嵌套仅占极少数(柱状图右端骤降)
存在一个深度7的异常值

针对次数据,本次编程的突出问题为:
1.没有注释导致可读性较低
2.Controller.getNextFloor()方法复杂度过高
针对问题1,其实我在编译器中码代码时是有注释解析的,因为各种方法的调用确实有些复杂,有了注释在调试时可以省去大量梳理逻辑的时间,也防止了遗忘逻辑细节的情况的发生,但是我的注释是单独回车了一行来写的,导致注释也占用了代码行数,如果提交带注释的版本会超出题目设定的最大行数,因此我去除了所有注释后才提交的,现在我有了经验,下一次提交我会带有详细的注释的。
针对问题2,我不可否认Controller.getNextFloor()这个方法深度过高,因为这个方法内部不仅拥有着大量if条件判断,同时也调用着getClosest(Integer a, Integer b)方法来进行协作,因此导致复杂度过高,但我在现在已经有了部分优化的思路,其中大量的if条件判断可以进一步进行精简和提高可读性,我也正在尝试这件事情。

第七次题目集:
对之前电梯调度程序再次进行迭代性设计,加入乘客类(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
在第七次题目集中重点将外部类改为了乘客类(Passenger),很明显,前一代的外部请求仅是有一个模糊的请求方向,但乘客类则将其改为了具体的目标楼层,使得电梯系统对于任务的执行更加精确。而在编程实现方面个人认为难度没有前两次的大作业高,因为在前两次的作业中电梯的框架已经搭建起来了,这次题目的修改仅是针对外部类的更改,对于运行的逻辑细节影响不大,只不过是加上一个“对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)”这一更改而已,只需小改一下removeRequests(int currentFloor)方法即可,剩下的逻辑细节几乎没有变动。
代码分析如下:
通过分析软件的分析可以得出如下数据:

总结如下:

<1>代码规模
代码行数:436
有效语句数:267
类/接口数:7
<2>复杂度指标
最高复杂度方法:​​Controller.getNextFloor()​​(第279行),复杂度19
最深代码块深度:7(第248行)
平均块深度:2.43
<3>代码质量
注释行占比:0.0%(无注释)
分支语句占比:21.0%
方法调用语句:140
类平均方法数:6.00
方法平均语句数:4.76
<4>雷达图
​​Kiviat雷达图特征​​
注释占比(% Comments)为0,雷达图在此维度塌陷至中心
最大复杂度(19)与最大块深度(7)突破外环警戒区
方法/类(6.00)指标居中,类规模分布相对均衡
​​块深度分布​​
深度0-2的浅层代码占比80%(健康基线)
深度≥5的深层嵌套仅占极少数(符合"浅嵌套优先"原则)
​​深度7的异常值​​(第248行)需重点审查,存在过度嵌套风险

针对次数据,本次编程的突出问题为:
1.没有注释导致可读性较低
2.Controller.getNextFloor()方法复杂度过高
3.最大深度7,存在过度嵌套风险
针对问题1与2,与第六次题目集的分析一样,上一次代码中的这两个问题并没有得到很好的解决。
针对问题3,我知道问题出现的原因,原因在于shouldStop(int floor)方法中的if else嵌套实在是太深了,不过我已经找到了改进优化的方法,因为其中的一些判断是为了确定方向的,我应该将这部分逻辑整合进determineDirection() 方法中,这样就可以解决shouldStop(int floor)过度嵌套的问题,同时也提高了代码的可读性。
三.采坑心得
个人认为大作业题目集中最大的坑还是在于电梯的运行处理逻辑,我最初在编码时是采用的SCAN电梯算法,因为我认为这个算法更符合现实中电梯的运行逻辑,但事实并不是这样,题目集中电梯的处理逻辑仅看两个队列的头部,这与我预想的运行处理逻辑不符,这就导致了我后续又需要花费很多时间在更正我的代码逻辑,严重拖慢了我的做题速度。因此我得出了一个惨痛的教训,在做题目之前一定要详细地分析测试样例,必须先把测试样例吃透才能继续进行构思,不然就很容易功亏一筹,构思与设计才是编程中的第一要务。同时在具体的编程实现上也碰到了一些问题,主要在于边界条件与特殊情况的考虑不够周到,而即使在大量的调试之后终于找到了问题所在,也因为急于想要解决问题的心理,各种在源码上“打补丁”,没有顾全大局,只是一心想要通过测试点,这就导致了代码的可读性变差、复杂度提高,不利于后续的迭代和优化。具体的例子就是我代码中的shouldStop(int floor)方法,这段方法中的过度嵌套就是我“补丁”打太多了的结果,尽管可以处理问题,但可读性极差。因此在之后的题目集中如果要对代码进行更改,一定要着眼总体,顾全大局,杜绝shouldStop(int floor)型方法的再次出现。
四.改进建议
针对代码的改进,重点针对代码中的突出问题
1.没有注释导致可读性较低:
在编码的过程中要养成写注释的好习惯,在提高可读性的同时也是在利人利己。
2.Controller.getNextFloor()方法复杂度过高:
我已经分析了Controller.getNextFloor()方法中复杂度高的原因,解决方法重点在于减少条件判断的依赖,据我分析,其中的部分判断语句完全可以进行优化调整。
3.最大深度7,存在过度嵌套风险:
我对源码进行分析,已经发现了问题出在了shouldStop(int floor)方法中,这是我多次“打补丁”的恶果,解决点在于将部分判断方向逻辑整合进determineDirection() 方法中,既解决了shouldStop(int floor)过度嵌套的问题,又提高了代码的可读性。
五.总结
通过这三次大作业的实践,我对Java类设计、复杂逻辑处理有了更深刻的理解。尤其是在电梯调度系统的实现过程中,我认识到以下几点核心收获:
​​需求分析的重要性​​
电梯的运行逻辑与常规认知存在差异,这迫使我必须从测试样例出发,反复验证每一步的设计是否符合题意。这让我意识到:​​脱离需求文档的编码是危险的​​,只有通过详细分析输入输出规则,才能构建正确的程序骨架。
​​单一职责原则的实践价值​​
第六次作业的类拆分让我体会到,将"电梯状态管理"、"请求队列处理"、"调度算法"等职责划分到不同类中,不仅降低了代码耦合度,也使得调试过程更加清晰。例如通过独立的Controller类集中处理方向决策,避免了Elevator类的复杂化。
​​复杂条件逻辑的优化方向​​
在解决getNextFloor()高复杂度问题时,我找到了自己的优化路径,并在尝试进行优化。

posted @ 2025-04-19 13:12  NJD很无聊  阅读(48)  评论(0)    收藏  举报