题目集5-7总结
前言:
第一次Java大作业终于结束了,对我来说真是个不小的挑战。但同时我也学到了很多:
- 熟悉了各种Java语法;
- 初步了解面向对象编程与面向过程编程的区别;
- 认识到单一职责对程序的重要性;
- 进一步练习了利用IDE进行改错;
- 强化了面对问题解决问题心态。
题目集一:简直就是当头一棒,一直到最开始的截止时间根本没有人能拿满分,老师们也是为此解释了很多,延期后我也是卡着点才交上去的。
- 知识点:类的设计,电梯运行逻辑的理解;
- 题量:共五个小题,前面四个题很容易编写,最后一题大作业可太大了;
- 难度:题目中标注的是中等,对刚接触这门语言的我实际编写起来只能说是难于上青天了。
题目集二:在题目集一的基础上进行迭代,有了前一次的基础,通过测试点还是可以勉强做到。
- 知识点:单一职责原则,类的分工,对错误或不合理数据进行处理;
- 题量:共三小题,同样的几乎所有精力都花在最后一题上了;
- 难度:题目中标注为较难,但感觉比第一次要容易些?
题目集三:最后一版迭代,要求添加一个乘客类,取消乘客请求类,对外部请求格式修改,我修改了非常非常非常多版,和两个样例的输出结果一样,自己也找出来了两个bug并修补成功,但始终只能通过一个测试点。
- 知识点:对类的修改,重构;
- 题量:同样把精力放在大作业上,但最后还是只过了一个测试点。
- 难度:题目中标注为较难,我也有同样感受,虽然样例测试很容易就能成功,但最终测试点就是过不去。前后发现了输入中带有空行代码无法处理和重复请求判断逻辑的严谨问题,成功解决,但并不是测试的点。(身心俱疲了.....)可能测的是处理请求的逻辑?但是我也是按照题目标准来的,好难猜啊。
设计与分析:
第一次作业:
- 目标:设计一个单部电梯调度程序
- 算法:LOOK算法
- 复杂度分析:

(1)代码模块
- 总行数:320行,对于这个程序来说文件相当大了(听说有人144行就过了)。
- 语句数:157个,数量还是偏高。
(2)代码复杂性:
- 最大复杂度:7,这个值是比较高了,说明代码逻辑相对混乱。
- 平均复杂度:7.00,相当高的值,虽然可以通过测试点,但代码的复用性很低。
- 最复杂方法:Main.main(),复杂度为7,说明该主方法处理问题过多,有待优化。
- 最复杂方法行数:269,相当高的值,说明方法过于复杂。
(3)代码注释:
- 含注释行占比9.1%,比例较低,说明注释不足,可读性有待提高,可能对维护造成困扰。
(4)代码块深度:
- 最大代码块深度:6,相对高的值,代码中存在较深的嵌套,对维护造成不便。
- 平均代码块深度:2.92,相对高的值,总体在可控范围内。
(5)分支和方法调用:
- 分支语句百分比:26.1%,比例相对适中,代码中运用了相当一部分分支逻辑,但好在不算太复杂。
- 方法调用语句数:55个,比例不算太高,整体上保证了代码性能。
第二次作业:
- 目标:对电梯类进行迭代,解决电梯职责过多的问题,并实现错误信息和重复请求的过滤。
- 算法:LOOK算法。
- 类图:

- 复杂度分析:

(1)代码模块
- 总行数:235行,相对于第一代更加简洁,更加有条理了。
- 语句数:136个,对于行数来说语句数偏多。
(2)代码复杂性:
- 最大复杂度:3,这个值相对合理,说明这版代码逻辑清晰,单一职责原则真的对代码优化了很多。
- 平均复杂度:3.00,相对合理的值,让代码清晰严明,可读性高。
- 最复杂方法:Main.main(),复杂度为3,相对于上一版明显优化。
- 最复杂方法行数:211,相对于上一版有所改善,但总体来说依然偏高。
(3)代码注释:
- 含注释行占比3.4%,比例非常低,注释非常少,几乎只注释了每个类的名字。
(4)代码块深度:
- 最大代码块深度:4,相对来说有所改善,维持在合理范围内。
- 平均代码块深度:2.03,合理范围,总体可控。
(5)分支和方法调用:
- 分支语句百分比:18.4%,比上一版更加优化,代码中运用了分支逻辑,但并不复杂。
- 方法调用语句数:65个,比例较高,说明方法调用偏多,代码读起来可能会比较困难。
第三次作业:
- 目标:再次迭代,取消乘客请求类,新增乘客类。
- 变动:
- 乘客请求输入变动情况:外部请求由之前的
<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层> - 对于外部请求,当电梯处理该请求之后(该请求出队),要将
<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)
- 乘客请求输入变动情况:外部请求由之前的
- 算法:LOOK算法。
- 类图:

- 复杂度分析:

(1)代码模块
- 总行数:306行,行数又变的好多,因为加了很多检测非法输入的功能。
- 语句数:164个,中规中矩,毕竟最后加入了很多判断,但还是有待改善。
(2)代码复杂性:
- 最大复杂度:9,这个值又变得很高,代码的可维护性降低,复杂性升高。
- 平均复杂度:2.59,相对合理的值,平均来看并没有太复杂,说明至少有一个方法过于繁琐。
- 最复杂方法:openCloseDoor().hasDownRequestsInRage,复杂度为9,这个方法过于繁琐,可能对代码的功能造成了影响。
- 最复杂方法行数:179,相对之前的版本,这版行数最少,但复杂度还是偏高。
(3)代码注释:
- 含注释行占比11.8%,比例还是比较少,真的不习惯在打代码是时切换输入法来输入汉字。
(4)代码块深度:
- 最大代码块深度:8,这部分代码深度较深,使得逻辑可能偏复杂,不便于后续修改。
- 平均代码块深度:3.08,深度较深,可能造成功能上的问题。
(5)分支和方法调用:
- 分支语句百分比:17.7,比上一版来说分支更少,使得运行压力减小。
- 方法调用语句数:75个,比例较高,说明方法调用再次增多,代码中的关系会更复杂。
踩坑心得:
第一次作业:
一开始像模像样写了好几天,一运行发现直接没有输出,不是编译错误就是运行超时,再要不然就是非零返回,好不容易改的能输出了,发现电梯停留的顺序和样例的还不一样,最后崩溃了,不停的改,不停的提交

当然其中一版输出结果和测试样例是对上了,怀着激动期待忐忑不安的心情点了提交后,绿色的“答案错误”确实格外扎眼,当时甚至想着是不是因为我最后一行多输出了一排空行导致结果不正确,于是在网上找了方法,千辛万苦去掉了最后一行的空行,并且保持了其他输出都与测试样例一模一样(心想这下算是完美无瑕天衣无缝了),提交后不负众望依然是0分啊。
后来决定不能盲目的改,发现电梯停留的顺序与测试样例不同后,从判断停留与开门的逻辑着手,再次分析题目,发现问题出现在逻辑上,我的代码判断停留的的方式并没有按照同向优先和就近一致的原则进行。
于是又是一番苦战:

那天中午吃完饭我就开始改代码,也要感谢老师助教们在群里发了很多启发性的内容,详细说明了电梯运行的逻辑,于是我仔细研读,发现电梯并不是一下将所有请求读入,而是将请求分为两个队列,每次都比较两个队列的头部,然后再做出决定,而我的代码直接读入了所有请求,造成停留和开门的顺序错误。
于是收拾好心情继续写呗。先是添加了打印的语句,检测每一步处理请求后两个队列中还剩下哪些请求,以及其究竟是符不符合要求。不出所料,在处理完内部请求的楼层后,对外部请求中相同的楼层的部分也进行了移除,明显这是不符合要求的。虽然题目中测试点能过,用了老师给的另外的测试样例,果然存在差异。知道了错在哪里也有目标了,继续。

最终在晚自习时改出来第一次成功的版本。这版在多给出的三个测试点中只有两个符合预期,还有一个顺序还是不对,抱着测试其是否运行存在问题的心理点了提交,最后居然答案正确了。
往往,最有把握的时候,结果会提醒你谦逊;最心灰意冷时,生活又会赐你惊喜。
然而呢,答案正确只是侥幸,有个样例没过确实事实,我也并没有侥幸过第二次的妄想,我知道我的代码还有很长的路要经历。
第二次作业:
所以,先在截止的题目集中,我先对第一次的代码继续加工,借鉴了同学的想法与建议,终于是把测试点都给对上了,答案也是对的。

有了这次经验,掌握了电梯运行逻辑。第二次作业是要保证单一职责,开始只完成了部分正确,后来想到过滤重复与不合理请求的代码可能存在错误,于是着重修改那部分。最后能正确完成内部和外部请求的筛选过滤,也是得到了答案正确。

对我来说第二次提交的代码是最满意的,条理清楚,简洁易读,完整的实现了功能。
在开始前就要做好充分的准备。要是用第一版侥幸过的代码直接来写肯定是越来越复杂,导致最后得不到预期的结果。
第三次作业:
可能也是沉浸在第二次的自我满足感中了,到最后都没办法通过更多测试点啊。
要求是取消乘客请求类新增乘客类,对请求队列的输入和处理也做出了改变。

虽说只过了一个测试点,但我也是改出了相当几个bug。

虽说都是部分正确而且过的测试点没什么区别,但是我确实对代码做出了实质性的改进。
public void processInput(String input) { if (input.equalsIgnoreCase("end")) return; // 拆分输入并去重 String[] requests = input.split("(?=[<>])"); HashSet<String> uniqueRequests = new HashSet<>(); for (String req : requests) { if (!req.isEmpty()) { uniqueRequests.add(req); } } for (String req : uniqueRequests) { try { Passenger newRequest = parseInput(req); if (newRequest.getTargetFloor() == 0) { // 内部请求 requestQueue.addInternalRequest(newRequest.getSourceFloor()); } else { // 外部请求 requestQueue.addExternalRequest(newRequest); } } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { System.err.println("输入格式错误: " + req); } } }
首先是这部分去重,一开始我是没办法对同一行输入的连续请求进行去重的,例如输入<5><5><5>并不能识别为<5>,并且会对后续结果造成影响,加入拆分输入并去重的代码之后就能实现连续输入并不影响后续电梯的运行。
while (true) { String input = sc.nextLine(); if (input.trim().isEmpty()) { continue; // 跳过空行 } if (input.equalsIgnoreCase("end")) break; controller.processInput(input); } controller.run(); }
以及这一段,最初输入请求中如果有空行会使整个程序无法运行,输出结果直接为空,于是有了跳过空行这一操作,最终也是无论怎么输入都会给出结果了。
还有很多对内部请求外部请求的判断、去重方法不一致问题,都得到了相应的改进和应对措施(当时改进之前的情况没有截图就不一一举例了)。但多次改进后三个测试点还是过不了一个(所以测的到底是什么啊啊啊啊啊啊啊啊啊啊啊啊)。
在输入和输出结果上我已经排查出了很多bug并加以改进,逻辑上我也添加打印信息的代码进行检测,实在是找不到哪里错了,改到最后一刻真的是失去所有力气和手段.....
虽然只拿了四分之一的分,但我觉得修改出的东西也是有点质量的,也是学到了对输入情况多方面的考虑以及应对措施,还是有收获的。
最终心得:
这三次作业踩的坑只能说是不少,代码这一块还是需要对工作十足的严谨和对工程要求的充分理解,必须是在完全get到要求后再开始动工才可以避免造成大量工作的浪费,导致后续要投入大量精力来修改的困境。心态同样也是个问题,面对成堆的问题和改了成千上万次也过不了的测试点还是要重新调整心情,积极面对,坚持下去,多改出哪怕一个bug也算是成功。
改进建议:
- 在几次的迭代中,我的代码注释一直偏少(因为写着写着就要换输入法真的很麻烦),现阶段代码规模相对较小还看不出来问题,后面规模增大,注释的重要性愈来愈明显。所以代码中应该有相应的注释来提醒自己各部分代码的功能,这样也使得其可读性和可维护性增高。
- 几次提交的代码都是开始没想清楚就开始写,写到最后才发现问题,越改越乱,导致最后自己也无法将这些问题结合成完整代码,导致代码割裂感很强。所以以后写代码要先设计符合题目要求的完整骨架,随后再进行填充,降低自己改代码的压力与难度。
- 代码复杂度还是偏高,可以进一步简化,减少分支,使得其适应更多情况,增强其复用性。
总结:
收获:
- 这三次作业是接触Java这门编程语言后进行的第一次完整程序设计,显然难度是不小的。最终也并没有完全通过测试点,说明代码存在功能上的硬性缺陷,这点仍需改进。
- 通过这三次作业的练习,我也学到了许多东西,积累了很多宝贵的编程经验。在开始编程之前一定要提前将题目要求理解透彻,先构建出具体可行的方案,有完整的思路再开始着手去做,而不是读一句话编一句话,到最后不符合要求又回过头去改,去加功能。编写时也要处处谨慎,注意类与类之间的关系,必要时添加注释,使得代码逻辑更清晰具体以及可读。调试代码时也要用恰当的方法,有理有据的修改,记住每一版代码可以留存备份,以备多次修改造成逻辑混乱无法实现最初的功能。
- 在面对问题、分析问题、解决问题的过程中,必然会遇到重重阻碍和种种不尽人意,这时候更需要我们坚守强大的内心和秉持不放弃的意志,静下心来反复去阅读题干,认真的去思考问题,最终肯定会对题目有更好更充分的理解。
- 同时,最后一次大作业没能通过,可能是在逻辑上与题目要求有别,在代码实现上过于复杂,对于这部分知识我还是要多加练习,在未来还需要更多的锻炼自己编码读码的能力。
建议:
- 本次题目集测试点有点少,要么满分要么零分,也并不知道测得是啥,以后是不是会有更多的测试点,能在修改完一个bug后得到相应的分呢
- 测试点中无法得知存在哪些问题,没法针对性的修改,改出bug后发现不是要测的点真的会有挫败感。

浙公网安备 33010602011771号