电梯题目及其迭代总结

一,前言

这三次电梯题目对于刚学习Java的人来说是一个很大的挑战,不仅在于类的设计以及职责的划分,也在于分析数据的处理以及核心算法的编辑。在初次上手电梯题目时遇到了许多困难,几天下来都是写改代码直到半夜。但是在自己完成电梯题目时,自己心中的喜悦也是不言而喻的。后面电梯题目的迭代是对第一次题目用面向对象的思想对类进行重新设计,让我们能够更加容易理解面向对象的思想和理念。总的来说,电梯题目及其迭代让我受益匪浅,不仅精进了我的代码能力,更让我进一步理解了面向对象的思想。

二,设计与分析

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

题目:

设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。

要求:

编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求)。

设计:

设计的类图如下:

类的分析:

  • Main类:使用正则表达式来正确读取每行输入当中所需的数据,然后按数据的形式来进行外部或者内部的请求的加入。
  • Request类:更好的存储和拿取外部请求的楼层与方向。
  • Elevator类:该类与外部请求类相关联,主要用来控制电梯的运行,和方向的确定,以及打印电梯当前所在的楼层、状态和运行方向。

sourceMonitor分析如下:

image
image

自我分析:

  • 注释比例低:“Percent Lines with Comments” 仅为 1.4% ,说明代码中注释极少,不利于代码维护、迭代。
  • 部分方法复杂度高:“Maximum Complexity” 为 12 ,“Average Complexity” 为 2.17 。像 “Elevator.processUpRequests ()” 方法圈复杂度达到 12,说明方法内逻辑分支(如 if - else、switch、循环等)较多,代码结构复杂。
  • 类与方法设计欠佳:平均方法数较多:“Methods per Class” 为 7.50 ,表明每个类包含的方法数量较多,说明一个类承担了过多不同类型的职责。
  • 方法深度问题:“Maximum Block Depth” 为 6 ,“Average Block Depth” 为 2.54 ,说明方法中嵌套层次较深,调试和理解代码会变得十分困难。
  • 总结:没有深刻的了解面向对象思想,在这次作业中的问题较多——类间关系的设计,类的职责以及算法结构的优化都存在不足。

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

题目:

对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客请求类、队列类以及控制类。

要求:

电梯运行规则与前阶段单类设计相同,但要处理如下情况:

  1. 乘客请求楼层数有误,具体为高于最高楼层数或低于最低楼层数,处理方法:程序自动忽略此类输入,继续执行
  2. 乘客请求不合理,具体为输入时出现连续的相同请求,例如<3><3><3>或者<5,DOWN><5,DOWN>,处理方法:程序自动忽略相同的多余输入,继续执行,例如<3><3><3>过滤为<3>

设计:

类图如下:

image

类的分析:

  • Elevator 类:用于记录电梯运行的关键状态信息。用于获取和设置电梯各种状态(楼层、方向、状态等),还能判断楼层是否有效 。
  • Controller 类:持有Elevator对象和RequestQueue对象,关联电梯和请求队列。负责协调电梯与请求队列,实现电梯运行逻辑控制。
  • RequestQueue 类:用链表分别存储内部请求internalRequests和外部请求externalRequests。可获取队列实例,以及获取和设置内、外部请求列表,还能添加内、外部请求 。
  • ExternalRequest 类:记录请求楼层floor和请求方向direction。
  • Direction 枚举类:定义了电梯运行方向的枚举值UP(向上)、DOWN(向下)、IDLE(空闲)。
  • State 枚举类:定义了电梯运行状态的枚举值MOVING(运行中)、STOPPED(停止) 。
  • Main 类:包含程序入口main方法,作用为正确读取每行输入当中所需的数据来储存内外部请求。

sourceMonitor分析如下:

image

自我分析

  • 注释缺失严重:“Percent Lines with Comments” 为 0.0% ,代码完全没有注释,极大地增加了代码维护和迭代的难度。
  • 这次代码设计十分混乱,类与方法方法设计存在问题,“Classes and Interfaces” 为 15 ,数量不算少,然而报告却显示 “No Methods Found”,说明存在类结构设计不合理的情况。
  • 总结:因为对于这一次的题目没有保持足够的重视,而且设计类和编写时没有很认真,导致出现了类结构设计不合理。

第三次电梯题目的设计与分析

题目:

对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客类、队列类以及控制类。

要求:

电梯运行规则与前阶段相同,但有如下变动情况:

  1. 乘客请求输入变动情况:外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
  2. 对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)

设计:

类图如下:

image

类的分析:

  • Elevator 类:用于获取和设置电梯的各种状态信息,还能判断楼层是否有效。
  • Controller 类:持有Elevator对象用于关联电梯,RequestQueue对象用于管理请求队列。,负责协调电梯与请求队列,实现电梯运行逻辑的控制。
  • RequestQueue 类:使用链表分别存储内部乘客请求internalRequests和外部乘客请求externalRequests,类型为Passenger。可获取请求队列实例,以及获取和设置内、外部请求列表,还能添加内、外部乘客请求。
  • Passenger 类:用sourceFloor记录乘客起始楼层,destinationFloor记录目的楼层。用于获取和设置起始楼层、目的楼层。
  • Direction 枚举类:定义了电梯运行方向的枚举值UP(向上)、DOWN(向下)、IDLE(空闲)。
  • State 枚举类:定义了电梯运行状态的枚举值MOVING(运行中)、STOPPED(停止)。

sourceMonitor分析如下:

image

自我分析:

注释方面

  • “Percent Lines with Comments” 仅为 2.2% ,注释占比极低。
  • 代码结构方面:平均语句数较多:“Average Statements per Method” 为 5.06 ,结合 “Methods per Class” 为 17.50 ,说明部分方法功能不够单一。
  • 最大代码块深度较大:“Maximum Block Depth” 为 6 ,“Average Block Depth” 为 1.92 ,说明代码中存在一定深度的嵌套结构。
  • 总结:虽然已经在添加注释,但是注释的占比还是很低。较于前两次来说,类间关系,代码结构,if-else的嵌套已经有所改进,但是还是存在改进空间。

三,踩坑心得

  1. 以后写代码应该提前分析好数据是怎么处理的,每个类应该有哪些功能。因为我一开始拿到题目时按照之前写简单题目的方法去写,先写一个大概,然后再去一点一点改进。但是这一次因为题目难度较大,实际修改时会发现修改起来会十分困难,而且一开始写的框架存在问题,导致后期修改时的难度已经不亚于重新写一次代码了。
  2. 应该尽量减少if-else的嵌套,if-else的嵌套不仅会使代码的可读性变得非常差,而且会使修改的难度指数级上升。在设计process方法的算法时,因为数据的处理需要考虑电梯的方向,内外部请求以及电梯此时所处的位置等等,导致一开始编写时使用了十分多的if-else,甚至不断嵌套。导致后面调试出现错误后,不仅阅读代码十分困难,修改起来也是十分耗费精力。后面只能重新编写,并将process方法拆分为了processRequests,processUpRequests和processDownRequests。
  3. 认真阅读题目的要求。一开始没有认真去看题目对于数据的处理是以输入为顺序的,而我将链表中的数据进行了重新排序,导致白白花费了将近一个晚上的时间。
  4. 创建链表应该在类的创建方法里进行。因为我一开始只是在使用链表之前才创建链表,但是有时候可能会忘记初始化链表,导致报错。而且多次创建链表也会导致代码出现重复,所有最好在一开始就初始化链表。
  5. 在使用链表时应该考虑链表为空的情况。在使用LinkedList中的getFirst方法来取出电梯内部请求队列头部和电梯外部请求来判断电梯的运行方向的时候没有考虑到队列可能为空的情况,导致提交了几次都显示我非零返回。上网了解后才发现,使用getFirst方法的时候如果链表为空的话就会出现非零返回的错误。

四,改进建议

  1. 现在应该摆脱C语言中面向过程的思想,这种思想对于Java的学习只有害处。在编写代码时会不自觉的以面向过程思想来思考,导致各个类之间的耦合度较高,每个类的职责也不够清晰,没有满足单一职责原理。不仅使代码的可读性变得很差,而且一旦出现错误或者需要改进时,修改代码的难度会变的很高。
  2. 以后再编写代码之前应该提前分析好每个类的作用,以及将类中的方法做到单一职责。代码分析中可以看出即使在第三次作业中也有语句过长以及代码块深度较大的问题,而且确实在修改时难度很大,总是修改一个地方就要带着修改很多东西,耗费了许多时间。
  3. 应该养成添加注释的好习惯。因为我们不可能只用一个晚上就将代码完善完整,在隔了一段时间重新编写代码时,如果没有注释的话,我们又要多花一定的时间重新阅读代码来明白每个类的职责。

五,总结

收获:

电梯题目及其迭代让我对面向对象思想有了更加深刻的认识以及理解。让我明白了设计,以及单一职责的重要性。而且因为本身的算法也是十分有难度,我的代码能力也有所提示。对数组,链表和正则表达式等语法的使用更加熟练。但是代码复用性还是很差,以及修改还是存在一定难度,还是要继续精进。

建议:

虽然在以后的工作中需要我们自己去分析用户需求来设计代码,但不过因为我们刚刚入门,对用户的分析可能不太到位,导致我们可能会陷入死胡同或者浪费许多时间,希望老师能够在这方面提供一定的帮助让我们能够更快更好的设计出算法,不然很可能导致我们编写代码的热情被浇灭,甚至开始讨厌这门课程。

posted @ 2025-04-20 16:41  写不来MATH  阅读(47)  评论(0)    收藏  举报