第一次大作业--电梯 代码分析
一、前言:
第一次电梯题目,要求设计一个电梯类,包含状态管理、请求队列管理以及调度算法。主要考察逻辑思维能力及算法设计、复杂问题分析、解决能力及需求理解、代码编写与调试能力。我认为本次题目集难度是三次中最大的,因为算法的设计、逻辑的构筑、问题的分析与解决都需要解决,其中最耗时的就是逻辑的构筑与不断调试调整。
第二次电梯题目,要求在第一次题目的基础上实现迭代,为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(SRP)。主要锻炼逻辑思维能力、类的合理设计与结构、拆分与优化。在第一次设计的电梯类中,由于将状态管理、请求队列管理和调度算法等多个功能都集中在一个类中,导致这个类的职责过于繁重。这不仅使得代码的可读性和可维护性降低,还增加了后续扩展和修改的难度,解决此问题就是本次电梯迭代的主要目标。本次题目任务量和难度相较于第一次来说下降了很多,如果在第一次题目集中完成了逻辑设计,本次题目只需要做类的拆分设计。
第三次电梯题目,要求对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类。在遵循单一职责原则基础上对于代码设计有了不一样的要求,输入输出也有了一些改变,但是核心逻辑不变。主要考察类的设计与抽象、类之间的关系设计、电梯调度算法改变后的实现。如果前两次习题全部完成,本次题目集任务量和难度将均为最小的。
二、设计与分析:
1.输入:读取输入实用正则表达式,与直接输入再去除多余符号提取信息的方式相比,时间和空间复杂度都得到了大大提升。再保证输入格式正确的前提下,代码效率更高。
2.数据储存:使用队列储存输入请求,可以很好的遵循look算法,先进先出。扩容、增加、移除方便、Java中自带的方法可以很好利用。
3.逻辑设计:
题目逻辑(我的最终代码逻辑):内部请求和外部请求分为两个队列,每次比较两个队列第一位请求。移动规则
当电梯方向为向上 UP 时:
若当前楼层小于最大楼层且上方有请求(内部请求队列顶部楼层大于当前楼层 ,或外部请求队列中存在起始楼层大于当前楼层的请求 ),则电梯上升一层。
若当前楼层小于最大楼层但上方无请求,或当前楼层已达最大楼层,则改变方向为向下 DOWN 。
当电梯方向为向下 DOWN 时:
若当前楼层大于最小楼层且下方有请求(内部请求队列顶部楼层小于当前楼层 ,或外部请求队列中存在起始楼层小于当前楼层的请求 ),则电梯下降一层。
若当前楼层大于最小楼层但下方无请求,或当前楼层已达最小楼层,则改变方向为向上 UP 。
停靠规则
内部请求:当内部请求队列的第一个请求楼层与电梯当前楼层相同时,电梯停靠。
外部请求:
若外部请求队列的第一个请求的起始楼层与电梯当前楼层相同,且该请求目标楼层与起始楼层的方向和电梯当前运行方向一致(目标楼层大于起始楼层且电梯方向为向上 ,或目标楼层小于起始楼层且电梯方向为向下 ),电梯停靠。
若外部请求队列的第一个请求的起始楼层与电梯当前楼层相同,且电梯内部请求队列为空;或者电梯当前方向为向上时,没有更高楼层的内部和同方向外部请求,且该外部请求目标楼层小于起始楼层;或者电梯当前方向为向下时,没有更低楼层的内部和同方向外部请求,且该外部请求目标楼层大于起始楼层,满足这些反方向停靠条件时,电梯也停靠。
第一次题目集:
类图:

这个类包含了电梯的所有属性(如当前楼层、最大楼层、最小楼层、运行方向、状态等)和方法(如处理请求的方法、移动的方法等)。所有操作都集中在一个类中,类的职责较为复杂,体现了对逻辑思维能力和问题分析解决能力的高度要求。在设计这个类时,需要全面考虑电梯运行的各种情况,以及请求队列的管理和调度算法的实现。同时,类中的属性和方法之间的关系也需要花时间设计,以确保电梯的行为符合预期。
只要求设计一个电梯类,所有的操作都要求在这一个电梯类中实现。最核心的就是逻辑的思维能力和问题分析解决能力。
我在这次题目集中,对于题目提出的“电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求“理解错误,没能够正确的分析问题需求,为了实现更高效的请求队列的方向查找,错误地编写申请了三个队列(内部请求、外部向上请求、外部向下请求)。
SourceMontor:

1.文件共 210 行,其中可执行 Statements 有 140 行,代码规模相对适中
2.分支语句占比 27.1% ,比例相对较高,条件判断逻辑较多。
3. 有 63 条方法调用语句,代码中方法间存在一定的交互调用。
4.类和接口数量有 5 个,平均每个类和接口对应的方法数为 3.20 个 ,整体类和方法数量分布较为适中。
5.分支语句占比:达 27.1% ,意味着代码中条件判断(如 if - else、switch 等 )逻辑较多
6.每个方法平均语句数为 6.63 条 ,方法内部语句数量不算很少,不过我的代码中部分方法内部逻辑较为复杂,这也需要改进。
7. 大部分语句集中在深度较低(1 - 2 )的代码块中,但也有一定数量的语句分布在深度较高的代码块,代码存在一定程度的嵌套复杂性。
第二次题目集:
类图:

Elevator 类:负责电梯相关属性(如 minFloor、maxFloor、currentFloor 等 )和操作(添加请求、处理请求等 )的管理,基本符合单一职责原则,专注于电梯自身状态和请求处理逻辑。
ExternalRequest 类:仅处理外部请求相关信息(sourceFloor、destinationFloor ),职责较为单一,专注于封装外部请求的属性,为系统提供外部请求的数据模型。
Controller 类:承担电梯调度控制功能,包括确定方向、移动电梯、判断停靠等操作,职责明确。不过,其内部方法较多且逻辑复杂,不过存在职责不够清晰的情况。
Main 类:作为程序入口,负责读取输入并初始化电梯系统,职责单一,专注于程序启动和输入处理相关操作。
关联关系
Elevator 类与 ExternalRequest 类通过 externalRequests 属性建立关联,表明电梯与外部请求之间存在管理与被管理的关系,电梯需要处理外部请求。
Elevator 类与 Controller 类通过构造函数建立关联,Controller 类依赖 Elevator 类来实现电梯的调度控制。
依赖关系:Main 类依赖 Elevator 类来初始化电梯系统,通过创建 Elevator 对象并调用其方法,实现程序的功能。
SourceMonintr:

1. 文件共 432 行,其中可执行的 Statements 有 263 行,代码量偏大。
2. 分支语句(如 if - else、switch 等)占比 14.8% ,这也是一我的改进一大重点内容。
3. 有 152 条方法调用语句,表明代码中方法间的调用较为频繁,体现了一定的模块化思想
4. 仅 0.9% 的行有注释,注释严重匮乏。这会极大地影响代码可读性与可维护性,他人或后期自己阅读、修改代码时,难以快速理解代码逻辑。这是由于我的代码量过大,不删去 注释就超出了限制,后续需要改进。
5. 每个方法平均 3.14 条语句,方法语句数不算多,但结合类与方法数量,方法功能有点分散。
6. 最复杂方法在 235 行,是 Controller.getNextFloor() 方法,Maximum Complexity 为 12 。该方法复杂度高,逻辑嵌套深,是优化重点。
第三次题目集:
类图:

Elevator 类:包含电梯相关属性(如 minFloor、maxFloor 等)和众多方法(如 addInternalRequest、processRequests 等 ),负责管理电梯状态、处理内外部请求以及执行相关操作,是电梯系统的核心类,职责相对集中,不过较为复杂。
Controller 类:持有 Elevator 实例,主要负责电梯调度控制,如 processRequests、determineDirection 等方法,承担电梯运行逻辑的控制职责。
Passenger 类:封装乘客相关信息(sourceFloor、destinationFloor ),代表乘客请求,职责单一明确。
Direction 枚举类 和 State 枚举类:分别定义电梯运行方向和状态的取值,为系统提供标准化的方向与状态标识,使代码更具可读性和维护性。
关联关系:Elevator 类与 Controller 类通过构造函数建立关联,Controller 依赖 Elevator 实现调度;Elevator 类与 Passenger 类通过请求处理逻辑关联,电梯需处理乘客请求。
依赖关系:Main 类依赖其他类进行系统初始化与功能调用,作为程序入口,创建相关对象并启动程序流程。
SourceMontor:

- 文件总共有 464 行,其中可执行代码行(Statements)为 279 行 。代码行数较多意味着逻辑复杂,维护难度相对较大。
2. 分支语句(如 if - else、switch 等)占比 23.3% 。较高的分支语句占比通常意味着代码逻辑判断较多,可能存在复杂的条件判断逻辑,这也说明了我的代码的可读性与维护性不太好。
3. 方法调用语句有 212 条。文件中方法之间的调用频繁程度,调用多说明代码的模块化和功能拆分有一定体现。
4. 仅有 3.2% 的行包含注释。注释比例极低,这会严重影响代码的可读性和可维护性,他人阅读或后期自己维护代码时,难以快速理解代码逻辑。
5. 文件中包含的类和接口数量为 6.67(可能存在部分未完全统计或计算的情况 ),平均每个类的方法数为 5.88 。类和方法数量较多,需要关注类与方法的职责划分是否合理,避免出现职责混乱。
6. 最复杂方法位于 171 行,是 Controller.move() 方法,其复杂度最高(Maximum Complexity 为 12 )。这提示该方法可能存在逻辑过于复杂、嵌套过多等问题,这也是后期代码优化的重点关注对象。
三、踩坑心得:
第一,需求分析是所有代码的基础和前提。需求分析一旦有误,代码的算法设计、类的设计、类的关系设计甚至于数据储存方式全部需要改正,整个代码基本都需要重构,之前的设计与分析全部作废。在这次题目中,我在充分理解电梯规则之前直接就开始编写代码,最后分析规则的时间在后续代码设计修改时付出了四五倍。这次大作业使我最充分地体会到,面向对象需求分析的重中之重,不可以再自大地先入为主。
需求分析错误最直接的显示就是输出数据不对应:


第二,类的设计与关系设计要合理合规,类设计符合单一职责原则,类关系设计符合高内聚低耦合原则,使得代码逻辑清晰、结构严密,增加代码高效性,避免不必要的代码重复。在不断修改代码设计过程中,每次需要修改时,由于类关系设计的不合理、封装设计的不合理,导致代码逻辑混乱,修改困难,可读性和可复用性低下,每次修改都需要把一个甚至多个
方法从开头开始重构。

比如这里只是开关门问题出现错误,但是我修改时需要从检查请求方法就开始调试
第三,注释是一段代码可读性、可维护性的重要实现方式,在不断修改调试电梯程序过程中,我真正认识到代码注释的重要性。缺乏注释的代码要修改和调试十分复杂麻烦,可读性和可维护性极低。在修改过程中,由于代码长度的限制我删去了很多注释,在满足代码长度要求的同时也加大了代码修改的复杂度,出现了一段代码反复修改,实际上却只是在两种逻辑中反复横跳而没有上升。
第四.对于边界值特殊值的处理应当给出提示,使得陷入死循环时可以及时发现并且解决问题。
在此次题目中,我在前期逻辑设计过程中多次陷入不同楼层和请求处理的死循环中,我发现如果不对边界值进行特殊输出,即使idea里有所提示,实际解决时仍然会很麻烦。
运行超时使我本次习题前期中遇到的最多的问题,其中包括第一层初始化静止导致的初始循环,以及到达某一层更新方向不及时的静止循环等等,在合理使用单独调试工具和边界值测试设定后解决。



四、改进建议:
1.对于代码深度高复杂度强的问题,我应当改进算法设计,减少if else语句的使用。另一方面也要通过对类的设计及其方法设计调用合理实现深度降低。
2.对于边界值特殊值的处理应当给出提示,使得陷入死循环时可以及时发现并且解决问题。
3.注释必须要写,给出必要的注释才能在维护代码修改代码时实现高效率高精准。
4.空参构造十分重要。当创建对象时,如果没有指定其他参数,空参构造函数会被调用,它可以将对象的属性设置为默认值,确保对象在创建后处于一个已知的初始状态
五、总结:
1.学习收获
通过这三次电梯题目集的练习,我深刻地认识到了需求分析的重要性。在开始编写代码之前,必须充分理解问题的需求和规则,否则可能会导致后续的大量返工。同时,我也学会了如何运用面向对象的设计原则,如单一职责原则、高内聚低耦合原则等,来设计合理的类结构和类关系。这不仅提高了代码的可读性和可维护性,还增强了代码的可扩展性。此外,我对代码的调试和优化也有了更深入的理解,认识到注释、边界值处理等方面对于代码质量的重要性。
2.不足之处
在类的设计和关系设计方面,我还存在一些不足之处。有时候仍然会出现类的职责不够单一,或者类之间的耦合度过高的问题。在算法设计上,对于复杂问题的处理还不够熟练,if else 语句的使用有时过于频繁,导致代码的复杂度较高。在注释方面,虽然认识到了其重要性,但在实际编写代码时,仍然会因为各种原因(如代码长度限制)而忽略注释的编写。此外,对于边界值和特殊值的处理还不够细致,有时会导致程序出现死循环或其他异常情况
3.建议
希望老师能多给一些测试用例,帮助我们找到过不去的测试点遇到的问题。

浙公网安备 33010602011771号