电梯迭代作业Blog
前言
在经历上个学期的面向过程程序设计学习后,我们开始了新一轮的面向对象程序设计,这个学期我们所用的编程语言是JAVA,在自学了JAVA的语法结构后,我们正式步入了面向对象程序设计的学习。完成前三次pta的语法巩固作业后,我们开始了第一次的大作业,每一次大作业都分为四个阶段,三次迭代作业和一次Blog作业。由于是第一次大作业,也是第一次接触面向对象的概念,在面对这次大作业的电梯迭代程序时,我体会到了深深的无力感,这种作业对我来说难度实在是太大了,以至于我我一度失去信心,想要放弃,但是在老师和同学的鼓励下,我还是坚持下来,不断地去尝试,不断地去修改,即使我的迭代作业完成的并不完美,我依然收获了许多。
在本阶段的程序设计学习中,我们围绕 单部电梯调度系统 进行了三次逐步迭代开发,随着题目要求的不断深入,项目的复杂度也在逐渐上升。下面我从知识点、题量以及难度三个维度,简要分析这三次迭代作业。
第一次迭代作业是基础的电梯调度程序,注重基本流程实现,第二次引入控制器与请求队列,强调了逻辑性和系统性,第三次则进一步模拟了真实的用户行为,将电梯系统变为了多类协同的真实系统,综合难度最大,也最具锻炼价值。三次作业题目数量不多但层层递进,覆盖了面向对象设计、类职责划分、请求调度、状态管理等关键知识点。这篇Blog就是我对这三次作业的总结和心得。
设计与分析
第一次电梯调度程序
第一次的题目:
设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。
请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求),具体运行规则详见输入输出样例。
输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:
运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
输入样例:
在这里给出一组输入。例如:
1
20
❤️,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
设计
第一次迭代的任务是:根据输入的电梯最小楼层、最大楼层和多个请求,模拟电梯的运行过程,并按照指定格式输出每一步的运行状态和开关门操作。
我按照的流程图是:

相应功能的实现如下:


分析
以下是SourceMonitor的分析:

这次设计的主要功能是模拟电梯在不同方向上的运行,因为不涉及面向对象设计,所以我的核心逻辑集中在main()和少量函数中。关键的实现在于基于输入判断运行方向(UP 或 DOWN)以及使用简单的条件语句和循环打印运行过程。这是我的实现代码:

以及:

第一次迭代强调对电梯调度基本流程的掌握和格式化输出的准确性,但在设计层面上仍处于起步阶段。它为后续引入对象、状态管理、请求队列等复杂机制奠定了基础。
第二次电梯调度程序
第二次的题目:对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(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
设计
第二次电梯程序设计参考的类图是:

第二次的设计引入了面向对象设计,新增了多个类如 Elevator、Request、Controller 等,实现单部电梯对内部与外部请求的调度。
主要类的结构为:
Elevator: 管理电梯状态,如楼层、方向、状态。
ExternalRequest: 表示楼层+方向的外部请求。
RequestQueue: 分别存储内部与外部请求,具备添加与去重功能。
Controller: 控制电梯运行流程、处理方向、移动与停靠等逻辑。
代码实现:



分析
第二次作业是电梯程序工程化的重要一步,结构清晰、职责明确、支持扩展,为后续引入乘客对象、队列优化等做了良好的铺垫。在第二次迭代中,程序按照SRP原则进行了模块化改进,拆分为 Elevator、ExternalRequest、RequestQueue 和 Controller 等多个类。每个类只负责自身领域的逻辑,例如 ExternalRequest 仅封装楼层与方向的请求数据,RequestQueue 统一管理内部与外部请求队列,Controller 负责调度电梯行为。但是这次的迭代依然有很多缺点,例如缺乏对乘客实体的抽象,内外请求混杂处理,可读性不高,这些缺点将在下一次迭代中得到很大改善。
以下是我的SourceMonitor分析图:

第三次电梯调度程序
第三次作业题目是:
对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客类、队列类以及控制类。
电梯运行规则与前阶段相同,但有如下变动情况:
乘客请求输入变动情况:外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)
设计
这次设计参考的类图是:

本次作业引入了Passenger类,取消了Request 类,强化 SRP 原则。外部请求以 <原楼层, 目的楼层> 输入,电梯到达原楼层开门接人,自动将其目标楼层作为内部请求加入队列。
本次设计的主要类为:
Passenger 封装起始楼层和目标楼层,计算其方向
Elevator 控制运行状态并判断是否应该停靠
RequestQueue 分离外部请求与内部请求,分别处理
Controller 控制整体调度流程、管理乘客入与出
代码实现为:




分析
以下是我各个类的SourceMonitor分析图:





第三次的迭代引入了乘客类,模型贴近现实,外部请求与内部请求完全分离,面向对象结构清晰,弥补了第二次迭代的缺点,便于模拟乘客行为,提高请求管理灵活性与调度清晰度,有利于调试、维护。但是封装方式对我来说实在是太复杂,在实现的过程中我出现了无数的错误,未来我会尽力设计出更好的封装方式。
同时本次作业代表着电梯系统设计的进一步成熟,采用了乘客建模,代替了纯请求处理,大大提升了面向对象设计的合理性,满足了SRP和开放封闭原则,为未来电梯程序进一步的开发奠定了坚实的基础。
踩坑心得
在完成三次迭代作业的过程中我踩了无数的坑,犯了无数的错误,以下是我三次迭代作业中分别踩的坑以及踩坑心得:
第一次作业
第一次迭代作业中,我将所有的逻辑都耦合在了一起,全部堆叠在Elevator类中,导致代码混乱不堪,职责不明。一旦调度的逻辑变得复杂,比如添加请求太多或改变排序优先级,我的程序就特别容易出错,而且难以修改错误。
心得
在程序设计中应该特别注意结构设计,设计的结构越符合“单一职责原则”,越符合模块化设计,后续的扩展和改错也会更加简单。
第二次作业
第二次作业在设计时虽然引入了多个类,但是类与类之间的数据传递并不清晰,导致请求处理总是重复或者遗漏,出现漏接电梯请求的问题。下面是我的出错案例:

心得
在设计时,类与类之间的职责要分明,数据之间的传递要清晰,否则就会出现信息丢失或者逻辑错误。并且在输入请求时应该判断请求是否合理,要有一个好的输入验证机制。同时在设计判断请求的方式时要考虑判断的复杂度,尽量减轻调度核心的负担,避免造成逻辑漏洞。
第三次作业
在第三次迭代作业中,我将所有的请求统一为了Passenger对象,简化了数据结构,但同时也造成了新的问题:如何区分“外部请求已被接收”与“乘客未下电梯”的状态,一旦状态管理不清,就容易导致重复处理同一个请求,电梯多次回到同一楼层开门以及目标楼层未能正确加入内部队列等一系列问题。
心得
调度算法的核心是:“方向优先”,因此队列应该动态筛选当前方向有效的乘客,过滤方向一致的请求,提升逻辑准确性。
改进建议
1.虽然三次迭代我都尽力去遵循了单一职责原则,但是依然存在类与类之间耦合度太高以及边界模糊的情况。在第三次迭代时,我将乘客请求统一为Passenger类,但其实乘客本身并不是一个完整的状态,比如没有是否进入电梯,是否已经到达等状态,这些都是我可以改进的地方。同时在存储乘客的请求时,我使用的是LinkedList查找乘客请求,判断是否应改开门,这样做的效率太低,可以改进为效率更高的方法。
2.我所设计的有些方法过长,比如move()方法,包含了太多的功能,我应该将其拆分成多个方法。同时我的代码中出现了大量的 if-else嵌套以及多重循环嵌套,我可以减少嵌套的层级,提升代码的可读性。
总结
1.通过本阶段三次关于电梯调度程序的迭代练习,我加深了对面向对象程序设计原则的理解,同时掌握了类图建模,抽象逻辑,状态管理等能力。
2.我深入了解了题目中电梯调度程序所用的基本的LOOK算法,未来我会去尝试更加高效的调度算法。
3.从最初单一请求处理到后续引入内外部请求,再到最后引入乘客类的设计,每一次迭代我都在进一步去优化程序的单一职责原则以及系统的扩展性和可维护性,我深刻体会到了代码的良好结构有多么的重要。
4.最后我想说的是,这三次迭代让我对从需求到设计,再到代码实现以及后续的优化的完整开发流程有了深刻的认识,为我后续迎接更大的挑战,开发更为复杂的项目奠定了坚实的基础。

浙公网安备 33010602011771号