大学生的牛马日常

导航

NCHU_电梯调度算法

NCHU_BLOG_电梯调度算法

一、前言:

  本次作业的难度并不算很高,重要的是如何理解电梯的运作规律,通过对类图的分析,以及实例研究,很快就能搞明白电梯的运作规律。但是理解并不代表能写出来、写的好,第一次的题目在刚上手时有种无力感,似乎无论如何也捋不清线头在哪,脑中思绪也如同缠在一起的麻绳,怎么也解不开。但是经过同学的点拨,我通过对类的不同处理,将“颜色”不同的麻绳分开后,似乎自然而然就没有缠在一起了,后续两次的代码上手就更快了。

二、设计与分析

(一)1.1第一次电梯题目的设计与分析

1.1.1题目要求:
  设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
  电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
  使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。
  请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求),具体运行规则详见输入输出样例。
本次题目未提供类图、自己设计的类图如下:
image
1.1.2类图的设计与分析:
Main类:
  包含主方法,作为程序入口点
Direction枚举:
  定义电梯运行方向(UP、DOWN、IDLE)
Elevator类: 包含
  基本属性(最小/最大楼层、当前楼层、方向)
  两个队列(内部请求和外部请求)
  公共方法(添加请求、运行电梯)
  私有方法(选择目标、移动、处理请求等)
ExternalRequest内部类:
  封装外部请求信息
1.1.3心得与分析:
Source Monitor分析结果:
image
  由上图中分析结果不难得出代码的优劣:
优点:
  1.注释覆盖率良好:17.5%的注释率高于一般标准(15%)
  2.方法规模控制合理:平均每个方法6.9条语句,规模适中
  3.类设计合理:3个类/接口,方法分布均匀(3.33方法/类)
缺点:
  1.高复杂度方法风险
    圈复杂度10:远超推荐值(1-4优秀,5-7可接受,8+需要重构)
    可能包含:多重if-else嵌套、复杂条件判断
    影响:可读性差、难以测试、维护成本高
  2.深度嵌套问题
    最大块深度6:存在过深的代码嵌套
    平均块深度2.31:整体嵌套层次偏深
    位置:第174行附近存在深度嵌套
  3. 分支语句比例偏高
    分支语句占比23.2%:控制逻辑较复杂
    可能包含过多条件判断
改进措施如下:
  1.方法拆分:将大方法按功能拆分为小方法
  2.策略模式:考虑使用策略模式处理复杂的选择逻辑
  3.提前返回:使用卫语句减少嵌套层次
  4.增加单元测试:为重点复杂方法编写测试用例

(二)1.2第二次电梯题目的设计与分析

1.2.1题目要求:
  对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客请求类、队列类以及控制类,具体设计可参考如下类图。
image
电梯运行规则与前阶段单类设计相同,但要处理如下情况:
  乘客请求楼层数有误,具体为高于最高楼层数或低于最低楼层数,处理方法:程序自动忽略此类输入,继续执行
  乘客请求不合理,具体为输入时出现连续的相同请求,例如<3><3><3>或者<5,DOWN><5,DOWN>,处理方法:程序自动忽略相同的多余输入,继续执行,例如<3><3><3>过滤为<3>
  注意:本次作业类设计必须符合如上要求(包含但不限于乘客请求类、电梯类、请求队列类及控制类,其中控制类专门负责电梯调度过程),凡是不符合类设计要求此题不得分,另外,PTA得分代码界定为第一次提交的最高分代码(因此千万不要把第一次电梯程序提交到本次题目中测试)。
1.2.2类图的设计与分析:
  由于题目中已经给出了类图的大体设计,因此我们只需要根据给出的类图进行方法的填充就行,需要注意的是,与第一次不同,需要做到去除重复输入(但是仅仅是连续的重复需要去除若输入为<2><3><2>,此时第二个<2>也会正常进入内部请求队列,外部请求同理)
1.2.3心得与分析:
Source Monitor分析结果:
image
由图可知,此次的代码设计简直是天灾
缺点如下:
  1.圈复杂度21:远超危险阈值(10+为危险级别),这可能是一个处理所有逻辑的"上帝方法"。
  2.注释率仅7.8%:远低于推荐标准(15-20%)
  3.方法数/类:11.25:单个类承担过多职责,违反单一职责原则:类可能做了太多不同的事情
  4.方法调用语句125条:占全部语句的56.8%,存在过度分解或复杂的调用链
这些问题会导致:
  1.可读性差:其他开发者难以理解代码意图
  2.影响:极难测试、维护和调试,bug风险极高
改进措施如下:
  1.首先拆分高复杂度方法
  2.类职责拆分
  3.增加注释

(三)1.3第三次电梯题目的设计与分析(测试点0未通过)

1.3.1题目要求:
  对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客类、队列类以及控制类,具体设计可参考如下类图。
image
电梯运行规则与前阶段相同,但有如下变动情况:
  乘客请求输入变动情况:外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
  对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)
  注意:本次作业类设计必须符合如上要求(包含但不限于设计电梯类、乘客类、队列类以及控制类),凡是不符合类设计要求此题不得分,另外,PTA得分代码界定为第一次提交的最高分代码(因此千万不要把第一次及第二次电梯程序提交到本次题目中测试)。
1.3.2类图的设计与分析:
  和第二次一样,题目中给出了类图,与第二次的区别仅仅是将乘客请求类转变成乘客类,在输入时更改了输入的规则,其余并无不同。
1.3.3心得与分析:
Source Monitor分析结果:
image
本次代码的设计与以往类似,有部分优点也有部分缺点
优点如下:
1.良好的整体复杂度控制:
  平均复杂度为 2.23,处于较低水平,说明大部分代码结构简单、易于理解和测试。
2.合理的方法设计:
  方法平均只包含 4.07 条语句,符合“短小精悍”的设计原则,有助于可读性和可复用性。
  每个类平均拥有 5.86 个方法,职责分配相对均衡,没有出现“上帝类”。
3.较高的模块化程度:
  方法调用语句多达 120 条,表明功能被良好地分解到不同方法中,减少了代码重复。
4.可控的平均嵌套深度:
  平均块深度为 2.56,说明大部分代码的嵌套层次在可接受范围内,没有普遍性的“箭头式代码”。
缺点如下:
1.代码可读性与可维护性差:
  注释严重不足:仅 4.4% 的代码行有注释,远低于通常建议的 15-20%,这会给其他开发者(或未来的自己)理解代码意图带来巨大困难。
2.存在复杂的“瓶颈”方法:
  极高的局部复杂度:Controller.determineDirection() 方法的复杂度高达 12。这意味着该方法有大量分支路径(如 if-else),难以测试、理解和维护,是一个潜在的错误高发点。
  极深的局部嵌套:最大块深度达到 8(位于第 272 行),存在“嵌套地狱”,严重影响了代码的可读性,并增加了逻辑出错的概率。
3.代码质量不均衡:
  雷达图清晰地显示出代码质量存在“短板”。虽然平均值尚可,但被少数极端复杂的部分拉高,表明代码质量不一致,好坏差异巨大。
总结
  本次代码在设计上相较于前两次有了不错的进步,但在可读性文档和局部复杂逻辑的处理上存在明显短板。对于代码的不足应该优先重构最复杂的方法并增加注释,将能极大地提升代码的整体质量。

三、踩坑心得

  (一)切记不要先入为主的思考电梯的运行逻辑,也不要想复杂电梯的运行逻辑(即不要给请求队列进行排队操作)
  在第一次作业时,我因为对题目有一种完美的想法,将题目中的电梯与现实生活中电梯进行联想,导致在队列中队头的数据在出队时产生了歧义,在纠结中,我第二次翻看了pdf在逐字逐句的研磨下,得出了不需要将请求排队的结果(浪费了很长的时间)
  (二)在遇到错误时,不要盯着给出的示例,必要时要和已经通过测试点的同学进行交流,或许他提供的一个例子就能找出你代码的不足
  在第二次作业时,明明两个测试案例都通过了,但是在提交时,就是通不过测试点,在几番测试下,还是没能找到问题在哪。最后询问得知,重复删除只需要连续的重复删除,而不是在请求队列中所有的重复都删除。
  (三)在处理队头数据出队时,仅仅是简单的比对了两个队列的队头中前一个数据是否相同,会导致在出队时,会将两个不同方向的数据同时删除
  在第三次作业时,由于在出队时,只比较了内部请求的目的层与当前层还有外部请求的请求层与当前层,两个在同时成立时,会将两个队头同时删除,在进行一些例子测试时出现错误。

四、改进建议

  (一)在类图的设计时,将不同的类进行明确的分开,但是在需要整合到一起时,想清楚不同的类整合起来,区别是什么?相似点是什么?而不是单纯的合并。
  (二)码力不足,在碰到稍微复杂一点的算法时,只会使用最简单的if-else语句进行判断,很容易出现漏掉的情况,也很容易使代码出现奇怪的错误,在算法这一方面需要更加精进。
  (三)代码的性能底下,很多地方都需要遍历所有,才会得出结果,导致刚开始提交时会出现超时的报错,还是在算法上需要精进。
  (四)缺少错误处理,在输入错误或逻辑错误时,只能先依靠对结果的判断,再进行单步调试才能找到问题出现的点,会浪费很多时间。
  (五) 代码结构问题,Controller类职责过重,可以考虑分开这个类,或者创建新类来分担这个类的职责。

五、总结

不足

  (一)精进算法,在面对复杂问题时,选择更优的算法会快速的解决问题
  (二)写代码时应该勤加注释,方便他人,也方便自己阅读
  (三)在看到题目时,优先进行问题分解,方便做类的拆解

收获

  通过第一次大作业,学到了初步的问题分解、类的创建,在解决复杂问题时能够细小化,逐个击破,这是一次很好的提升。也提升了个人的心态,在无数次失败中,看到胜利的曙光,然后走向光明,这个过程很爽,也和痛苦。谢谢阅读!

posted on 2025-11-19 23:06  许是远山春色  阅读(37)  评论(0)    收藏  举报