对567电梯程序的总结与分析
前言
1.知识点
(1)面向对象设计(OOP)
单一职责原则(SRP):将类的职责分解为独立的模块。
类设计:设计电梯类、乘客类、请求队列类、控制类等。
类之间的关系:关联、依赖、组合等。
(2)电梯调度算法
优先处理同方向请求。
处理顺路请求。
状态管理:电梯的运行状态(移动、停止、开门、关门)。
(3)输入输出处理
键盘输入模拟乘客请求。
处理无效请求(如无效楼层、重复请求)。
输出电梯运行过程。
(4)数据结构
使用队列(LinkedList)管理请求。
内部请求和外部请求的区分。
(5)异常处理
忽略无效输入(如超出楼层数范围的请求)。
过滤重复请求。
2.题量和难度
第一次题目(单部电梯调度程序)
题量:设计一个电梯类,包含状态管理、请求队列管理以及调度算法。
难度:中等。
知识点:
电梯类的职责较多,需要处理状态切换、请求队列管理、调度逻辑等。
输入输出模拟乘客请求,输出电梯运行过程。
第二次题目(类设计迭代)
题量:对第一次题目进行迭代设计,分解类职责。
难度:较难。
知识点:
遵循单一职责原则,设计电梯类、乘客请求类、请求队列类、控制类。
处理无效请求和重复请求。
控制类负责调度逻辑。
第三次题目(类设计迭代,加入乘客类)
题量:进一步迭代设计,加入乘客类。
难度:较难。
知识点:
取消乘客请求类,改为乘客类。
外部请求格式变为源楼层和目的楼层。
外部请求处理后将目的楼层加入内部请求队列。
递进关系
第一次题目:基础设计,电梯类职责较多,主要关注调度逻辑。
第二次题目:迭代设计,分解职责,引入控制类管理调度逻辑。
第三次题目:进一步迭代,加入乘客类,优化类设计,处理外部请求的源楼层和目的楼层。
3.总结
知识点:面向对象设计、电梯调度算法、输入输出处理、数据结构、异常处理。
题量:三次题目逐步递进,从单类设计到多类设计,职责分解更加明确。
难度:从第一次的中等到第三次的较难,主要体现在类设计的复杂度和规则的处理上。
二.设计与分析
第一次电梯代码分析与类图


第二次电梯代码分析与类图
第二次代码与第一次相比加了一些限制输入的条件,所以类图不变


第三次电梯代码分析与类图
第三次改了一些逻辑,新增了passenger的类


三.踩坑心得
在三次电梯调度程序的设计与分析过程中,我经历了从简单到复杂、从基础到优化的设计演变。第一次设计时,电梯类职责较为单一,主要关注基本的调度逻辑,实现了电梯状态管理、请求队列处理和简单的输入输出功能,初步构建了程序的框架。第二次迭代聚焦于遵循单一职责原则,将电梯类的职责合理分解,引入控制类来管理调度逻辑,使类之间的关系更加清晰,增强了程序的可维护性,并在输入处理上增加了异常处理机制,提升程序健壮性。
第三次设计更具挑战性,引入乘客类后,对类之间的交互逻辑进行了重新梳理和优化。同时,适应输入格式的变化,调整了外部请求的处理方式,将其与内部请求有机结合,进一步完善了电梯调度的业务逻辑。然而,由于对电梯运行方向和停止判断的逻辑处理不够优雅,导致代码复杂度较高,过多的 ifelse 嵌套使得代码可读性和可维护性受到影响。这让我意识到在后续学习中,需要深入探索更高效的算法设计思路,以降低代码复杂度,提升代码质量。
第一次电梯调度程序
对于第一次大作业,因为知道逻辑但我不清楚如何设置关系,因此进展缓慢。到后来我写时总是过不去,代码逐个调试时才悟得要同方向优先处理,当处理完同方向时再处理另一方向,然后再根据外部或内部请求与当前楼层的距离,判断要到哪个楼层。
输入示例:
点击查看代码
1
20
<3,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
第二次电梯调度程序
第二次电梯调度程序加上了异常的处理,具体体现在两个方面:1.乘客请求不合理,具体为输入时出现连续的相同请求,例如<3><3><3>或者<5,DOWN><5,DOWN>,处理方法是程序自动忽略相同的多余输入2.乘客请求楼层数有误,具体为高于最高楼层数或低于最低楼层数,处理方法:程序自动忽略此类输入,继续执行
输入示例
点击查看代码
1
20
<3,UP>
<3,UP>
<5>
<5>
<5>
<6,DOWN>
<7>
<7>
<3>
<22,DOWN>
<5,DOWN>
<30>
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
Open Door # Floor 5
Close Door
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door
第三次电梯调度程序
这次代码把外部请求修改了,外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>,而且对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾),因此我重写了外部请求的类,新增添了乘客类。原本有4个测试点,我后面两个总过不去,但最后我不断地输入测试数据,发现了我的代码对输入单个请求时判断有缺陷,我修改后,不断调试,最终是把后面两个测试点也过了。
输入示例1
点击查看代码
1
20
<5,4>
<5>
<7>
end
输出示例1
点击查看代码
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
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
Current Floor: 5 Direction: DOWN
Open Door # Floor 5
Close Door
Current Floor: 4 Direction: DOWN
Open Door # Floor 4
Close Door
输入示例2
点击查看代码
1
20
<5,9>
<8>
<9,3>
<4>
<2>
end
输出示例2
点击查看代码
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
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
Current Floor: 8 Direction: UP
Open Door # Floor 8
Close Door
Current Floor: 9 Direction: UP
Open Door # Floor 9
Close Door
Current Floor: 8 Direction: DOWN
Current Floor: 7 Direction: DOWN
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Open Door # Floor 4
Close Door
Current Floor: 3 Direction: DOWN
Current Floor: 2 Direction: DOWN
Open Door # Floor 2
Close Door
Current Floor: 3 Direction: UP
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Current Floor: 8 Direction: UP
Current Floor: 9 Direction: UP
Open Door # Floor 9
Close Door
Current Floor: 8 Direction: DOWN
Current Floor: 7 Direction: DOWN
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door
总结
在完成三次电梯调度程序作业的过程中,我踩过不少坑,但也从中收获了宝贵的经验。第一次作业时,我对类之间的关系把握不准,导致代码结构混乱,进展缓慢。在调试过程中,我逐渐意识到同方向优先处理请求的重要性,通过不断调整代码逻辑,才让电梯能够按照正确的方向和顺序运行。这让我明白,在设计程序时,清晰的逻辑规划至关重要。
到了第二次作业,增加了异常处理的要求,我开始着手处理无效请求和重复请求。起初,我没有充分考虑到各种异常情况,导致程序在面对一些特殊输入时出现错误。于是,我仔细梳理了输入的规则,增加了验证和过滤机制,对输入进行严格检查,确保只有合法的请求才能进入处理流程。这让我认识到,程序的健壮性需要在细节上多下功夫。
第三次作业最具挑战性,外部请求格式由<请求楼层数,请求方向>改为<请求源楼层,请求目的楼层>,并且要将目的楼层加入内部请求队列。我在处理这一变化时,最初没有充分理解新格式的逻辑,导致代码在处理请求时出现混乱。通过反复调试和测试,我发现了代码在处理单个请求时存在的判断缺陷,于是对相关逻辑进行了修改和优化。这让我深刻体会到,当需求发生变化时,必须全面重新审视代码,确保每个环节都能适应新的要求。
此外,我还意识到代码的复杂度逐渐增加,尤其是电梯方向判断和停止判断部分,过多的 ifelse 嵌套使得代码难以维护和理解。这让我下定决心,在后续的学习中要探索更高效的算法设计思路,以降低代码复杂度,提高代码的可读性和可维护性。
总之,这三次作业让我在不断地试错和改进中成长,不仅提升了编程技能,也培养了我解决问题的耐心和毅力。
这三次电梯代码我的核心是在每个楼层根据电梯运行方向,请求方向,与当前楼层的距离来决定方向和是否停止。这三次大作业我都是靠着这个判断。这是第三次电梯代码的关键部分:
点击查看代码
public void detDir() {
if (!que.getIReqs().isEmpty() && !que.getEReqs().isEmpty()) {
if (elv.getDir() == que.getEReqs().get(0).getDir() && elv.getDir() == Dir.UP) {
if (elv.getCF() < que.getEReqs().get(0).getF() && elv.getCF() > que.getIReqs().get(0)) {
elv.setDir(Dir.UP);
} else if (elv.getCF() > que.getEReqs().get(0).getF() && elv.getCF() < que.getIReqs().get(0)) {
elv.setDir(Dir.UP);
} else if (elv.getCF() > que.getEReqs().get(0).getF() && elv.getCF() > que.getIReqs().get(0)) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() < que.getEReqs().get(0).getF() && elv.getCF() < que.getIReqs().get(0)) {
elv.setDir(Dir.UP);
}
} else if (elv.getDir() == que.getEReqs().get(0).getDir() && elv.getDir() == Dir.DOWN) {
if (elv.getCF() > que.getEReqs().get(0).getF() && elv.getCF() > que.getIReqs().get(0)) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() < que.getEReqs().get(0).getF() && elv.getCF() > que.getIReqs().get(0)) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() > que.getEReqs().get(0).getF() && elv.getCF() < que.getIReqs().get(0)) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() < que.getEReqs().get(0).getF() && elv.getCF() < que.getIReqs().get(0)) {
elv.setDir(Dir.UP);
}
} else if (elv.getDir() == Dir.IDLE) {
Passenger eReq = que.getEReqs().getFirst();
if (eReq.getF() > elv.getCF()) {
elv.setDir(Dir.UP);
} else {
elv.setDir(Dir.DOWN);
}
} else if (elv.getDir() == Dir.DOWN && que.getEReqs().get(0).getDir() == Dir.UP) {
if (elv.getCF() > que.getEReqs().get(0).getF() && elv.getCF() > que.getIReqs().get(0)) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() > que.getEReqs().get(0).getF() && elv.getCF() < que.getIReqs().get(0)) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() < que.getEReqs().get(0).getF() && elv.getCF() > que.getIReqs().get(0)) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() < que.getEReqs().get(0).getF() && elv.getCF() < que.getIReqs().get(0)) {
elv.setDir(Dir.UP);
}
} else if (elv.getDir() == Dir.UP && que.getEReqs().get(0).getDir() == Dir.DOWN) {
if (elv.getCF() > que.getEReqs().get(0).getF() && elv.getCF() > que.getIReqs().get(0)) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() > que.getEReqs().get(0).getF() && elv.getCF() < que.getIReqs().get(0)) {
elv.setDir(Dir.UP);
} else if (elv.getCF() < que.getEReqs().get(0).getF() && elv.getCF() > que.getIReqs().get(0)) {
elv.setDir(Dir.UP);
} else if (elv.getCF() < que.getEReqs().get(0).getF() && elv.getCF() < que.getIReqs().get(0)) {
elv.setDir(Dir.UP);
}
}
} else if (!que.getIReqs().isEmpty() && que.getEReqs().isEmpty()) {
if (elv.getDir() == Dir.UP) {
if (elv.getCF() > que.getIReqs().get(0)) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() < que.getIReqs().get(0)) {
elv.setDir(Dir.UP);
} else if (elv.getCF() == que.getIReqs().get(0)) {
same = 0;
}
}
if (elv.getDir() == Dir.DOWN) {
if (elv.getCF() > que.getIReqs().get(0)) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() < que.getIReqs().get(0)) {
elv.setDir(Dir.UP);
} else if (elv.getCF() == que.getIReqs().get(0)) {
same = 0;
}
}
if (elv.getDir() == Dir.IDLE) {
if (elv.getCF() > que.getIReqs().get(0)) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() < que.getIReqs().get(0)) {
elv.setDir(Dir.UP);
} else if (elv.getCF() == que.getIReqs().get(0)) {
same = 0;
}
}
} else if (que.getIReqs().isEmpty() && !que.getEReqs().isEmpty()) {
if (elv.getDir() == Dir.UP && que.getEReqs().get(0).getDir() == Dir.UP) {
if (elv.getCF() > que.getEReqs().get(0).getF()) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() < que.getEReqs().get(0).getF()) {
elv.setDir(Dir.UP);
} else if (elv.getCF() == que.getEReqs().get(0).getF()) {
same = 0;
}
} else if (elv.getDir() == Dir.DOWN && que.getEReqs().get(0).getDir() == Dir.DOWN) {
if (elv.getCF() > que.getEReqs().get(0).getF()) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() < que.getEReqs().get(0).getF()) {
elv.setDir(Dir.UP);
} else if (elv.getCF() == que.getEReqs().get(0).getF()) {
same = 0;
}
} else if (elv.getDir() == Dir.DOWN && que.getEReqs().get(0).getDir() == Dir.UP) {
if (elv.getCF() > que.getEReqs().get(0).getF()) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() < que.getEReqs().get(0).getF()) {
elv.setDir(Dir.UP);
} else if (elv.getCF() == que.getEReqs().get(0).getF()) {
same = 0;
}
} else if (elv.getDir() == Dir.UP && que.getEReqs().get(0).getDir() == Dir.DOWN) {
if (elv.getCF() > que.getEReqs().get(0).getF()) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() < que.getEReqs().get(0).getF()) {
elv.setDir(Dir.UP);
} else if (elv.getCF() == que.getEReqs().get(0).getF()) {
same = 0;
}
} else if (elv.getDir() == Dir.IDLE) {
if (elv.getCF() > que.getEReqs().get(0).getF()) {
elv.setDir(Dir.DOWN);
} else if (elv.getCF() < que.getEReqs().get(0).getF()) {
elv.setDir(Dir.UP);
} else if (elv.getCF() == que.getEReqs().get(0).getF()) {
same = 0;
}
}
} else {
elv.setDir(Dir.IDLE);
}
}
我依靠它来判断电梯的状态,再根据我另一个部分判断是否停止,如果停止就删除头部请求:
点击查看代码
public void chckStps() {
int curF = elv.getCF();
Dir curDir = elv.getDir();
boolean stopped = false;
if (!que.getIReqs().isEmpty() && que.getEReqs().isEmpty() && que.getIReqs().getFirst() == elv.getCF()) {
remIReqs(curF);
opnDr();
stopped = true;
} else if (!que.getEReqs().isEmpty() && que.getIReqs().isEmpty()) {
Passenger firstReq = que.getEReqs().getFirst();
if (firstReq.getF() == curF) {
remEReqs(curF, curDir);
opnDr();
stopped = true;
}
} else if (!que.getIReqs().isEmpty() && !que.getEReqs().isEmpty()) {
if (que.getIReqs().getFirst() == curF && que.getEReqs().getFirst().getF() == curF) {
if ((que.getIReqs().getFirst() > curF && que.getEReqs().getFirst().getF() > curF) ||
(que.getIReqs().getFirst() < curF && que.getEReqs().getFirst().getF() < curF)) {
elv.setDir(Dir.IDLE);
}
Passenger firstReq = que.getEReqs().getFirst();
if (curDir == que.getEReqs().getFirst().getDir()) {
remEReqs(curF, curDir);
}
remIReqs(curF);
opnDr();
stopped = true;
} else if (que.getIReqs().getFirst() == curF && que.getEReqs().getFirst().getF() != curF) {
remIReqs(curF);
opnDr();
stopped = true;
} else if (que.getEReqs().getFirst().getF() == curF && que.getIReqs().getFirst() != curF) {
if (que.getEReqs().getFirst().getDir() == curDir) {
remEReqs(curF, curDir);
opnDr();
stopped = true;
} else if (elv.getDir() == Dir.DOWN && que.getEReqs().get(0).getDir() == Dir.UP) {
if (curF > que.getIReqs().getFirst()) {
elv.setDir(Dir.DOWN);
} else if (curF < que.getIReqs().getFirst()) {
elv.setDir(Dir.UP);
remEReqs(curF, curDir);
opnDr();
stopped = true;
}
} else if (elv.getDir() == Dir.UP && que.getEReqs().get(0).getDir() == Dir.DOWN) {
if (curF > que.getIReqs().getFirst()) {
elv.setDir(Dir.DOWN);
remEReqs(curF, curDir);
opnDr();
stopped = true;
} else if (curF < que.getIReqs().getFirst()) {
elv.setDir(Dir.UP);
}
}
}
}
if (stopped) {
elv.setSt(St.STOPPED);
System.out.println("Close Door");
}
}
四.改进建议
由于我的代码逻辑基本没什么变化,第三次大作业代码我为了与之前代码逻辑相符,输入后处理时还是将<请求源楼层,请求目的楼层>修改为<请求楼层数,请求方向>,按照之前的逻辑判断。因此核心代码(判断方向)复杂度极高,

这张图可以看出判断停止和判断方向两个部分非常复杂,主要是因为这两个部分我基本都是用ifelse嵌套,囊括了所有可能,因此很复杂。所以改进时应该用其他的思路来实现这个功能,从而达到降低复杂度的目的。另外,由于我对电梯运行时每上升或下降一个楼层都要根据请求判断此时电梯应该往哪运行,无形之中也更繁琐了。改进建议是根据内外的头部请求判断电梯最终到达哪个楼层,再直接输出上升或下降的过程。
五.总结
通过本阶段三次题目集的学习,我收获颇丰。在面向对象设计方面,掌握了单一职责原则,学会了合理设计类及其关系,如将电梯类、乘客类、请求队列类、控制类等进行分解,使职责更加明确。电梯调度算法的理解也逐渐深入,能熟练运用优先处理同方向请求和顺路请求等策略,并有效管理电梯的运行状态。同时,我强化了对数据结构(如队列)在请求管理中的应用,以及如何处理输入输出和异常情况,如忽略无效输入和过滤重复请求等。
然而,我也意识到自己仍有许多需要进一步学习和研究的地方。例如,代码的复杂度较高,尤其是电梯方向判断和停止判断部分,过多的 ifelse 嵌套使得代码可读性和可维护性变差,后续应探索更优的算法设计思路来降低复杂度。在类的设计上,还可以进一步研究更优雅的架构,以提高代码的复用性和扩展性

浙公网安备 33010602011771号