题目集5~7总结Blog
一、前言
这三次题目集围绕电梯调度系统展开,采用迭代开发的方式逐步完善功能。题目安排合理,难度呈阶梯式上升,很好地体现了"由浅入深"的学习路径。
- 题目集5中的作为基础版本,要求实现单部电梯的基本调度功能。主要考察类的基本设计、队列管理和简单调度算法。题目量适中,但电梯调度逻辑对我们初学者来说仍然颇具挑战性。
- 题目集6在基础版本上增加了输入验证和去重功能,同时强调类的单一职责原则。要求将原先集中在Elevator类的职责拆分到多个类中。难度有所提升,特别是需要重构原有设计。
- 题目集7进一步改进需求规格,引入Passenger类,修改外部请求格式,并增加请求转换逻辑。虽然题目量没有增加,但由于设计模式的变化,使得复杂度提高。
总体来看,这三次作业形成了一个完整的迭代开发过程:从基础功能实现(题目集5),到架构优化(题目集6),再到需求变更和功能扩展(题目集7)。这种安排既保证了学习的连续性,又逐步培养了面向对象设计和重构的能力。当然,对于刚刚接触JAVA学习的我们来说突然出现这样的题目还是非常具有挑战性的,同时也给我留下了深刻的印象。⊙﹏⊙∥以后看到电梯都忘不了了(划掉)
二、设计与分析
该部分我将对题集5~7中三个电梯程序分别从类设计、复杂度、主要问题和改进方面进行简要分析
题目集5:单部电梯基础版本
类设计分析
这是困扰了我们很久的电梯第一次出现在题目集中,第一次实现的类结构相对简单,主要包含:
①Elevator类:核心类,包含电梯状态、请求队列和调度逻辑
②Request类:表示外部请求
③Main类:处理输入输出
类图示意:

复杂度分析
虽然从类图上分析这个程序好像不复杂,但是实际编写的时候就能体会到想要实现题目要求的运行逻辑有多困难,以下是对我在不断尝试下最终的代码的分析,虽然没有通过测试,但是在编写的过程中仍然收获了不少:

使用SourceMonitor生成的报表显示:
• 方法平均复杂度:5.0
• 最大复杂度:5.0
• 代码行数:183行
主要问题:
Elevator类承担了过多职责,违反了单一职责原则。- 调度算法(determineDirection)过于复杂,嵌套层次深。
- 基础能力不够强,对题目还没有完全理解,导致程序的电梯运行逻辑与题目要求不符,无法通过测试。
改进心得
初次实现暴露了面向对象设计经验不足的问题,遇见稍微复杂的题目就像被下马威,一下子不知道从何开始
程序的结构比较混乱,特别是将所有逻辑集中在Elevator类中,导致:
- 修改困难:任何需求变更都需要修改原有代码
- Elevator类测试困难:方法间耦合度高,难以单独测试
- 扩展困难:添加新功能需要修改已有代码
1和3两点导致了后面在题目集7中迭代时几乎要把程序重新再构思一遍,可以说第一次的编写是我完全没有准备的尝试,暴露了自身能力的不足,也警醒了我要提高自身的能力。
题目集6:遵循SRP原则的改进版本
有了第一次题目集的经历,这次我更加认真地思考了如何改进
类设计分析
根据题目要求,将职责拆分到多个类:
• Elevator:仅管理电梯状态(楼层、方向等)
• RequestQueue:管理内部和外部请求队列
• Controller:处理调度逻辑
• ExternalRequest:表示外部请求
• Main:输入输出处理
类图示意:

复杂度分析
SourceMonitor数据显示:

• 方法平均复杂度:1.34 (显著改善)
• 最大复杂度:5.0
• 代码行数:339行
改进点:
- 职责划分更清晰,各类专注单一功能
- 调度逻辑从Elevator移到Controller,结构更合理
- 在进行了职责的拆分后,程序的复杂度指标明显改善,这让我体会到了“分工”的重要性
遗留问题:
- 这次的程序在IDEA和PTA的测试中看起来都没有什么问题(会赢吗?),但是提交系统提示后运行超时(并没赢),经过检查和修改后依然没有在题集截止之前通过测试 (:з)∠),看来还是需要进行改进。
- Controller类仍较复杂
- 某些getter/setter方法未被使用
改进心得
通过职责拆分,代码质量得到显著提升:
- 可维护性增强:修改一个类的实现不影响其他类
- 可测试性提高:可以单独测试每个组件
- 可读性改善:类和方法的功能更明确
但同时也发现,良好的设计需要更多代码量(从183行增至339行)增加的是代码减少的是头发,也需要花费更多的时间去思考职责如何分配,花更多精力去优化程序,当然这是提高质量的必要代价,是值得的。
题目集7:引入Passenger类的最终版本
经过前两次的经验和教训,这次的程序终于通过了pta的测试!以下是对该程序的分析:
类设计分析
在前一版本基础上:
- 根据题目要求移除
ExternalRequest类 - 新增
Passenger类表示乘客请求 - 修改请求格式为<源楼层,目的楼层>
- 增加请求转换逻辑
类图示意:

复杂度分析

SourceMonitor数据显示:
• 方法平均复杂度:1.52
• 最大复杂度:5.0
• 代码行数:339行
主要变化:
- 请求表示方式改变,更符合现实场景
- 增加请求转换逻辑(外部→内部)
- 整体架构保持稳定
- 完成题目所要求的运行逻辑
改进心得
这次迭代验证了前期良好设计的重要性:
- 架构稳定:核心结构无需大改即可适应需求变化
- 扩展容易:新增Passenger类和转换逻辑很自然
- 维护简单:修改集中在特定区域
同时也发现,随着功能增加,注释不足的问题显得突出,需要养成及时注释的习惯。
三、踩坑心得
该部分总结了一些我踩过的坑,也就是在编写过程中犯的错误。
1. 正则表达式错误
在题目集5中,处理外部请求输入时,正则表达式漏掉了逗号分隔符,导致无法正确解析输入。例如:
// 错误写法
String pattern = "<(\\d+)(UP|DOWN)>";
// 正确写法
String pattern = "<(\\d+),(UP|DOWN)>";
解决过程:通过打印解析后的数据发现匹配失败,仔细检查正则表达式后修正。
2. 调度算法理解错误
最初误以为电梯只需改变一次方向即可处理所有请求,导致提交好几次多次都未通过测试。实际上需要根据LOOK算法原则:
• 保持当前方向直到该方向无请求
• 然后才改变方向
教训:必须完全理解需求后再敲代码,不能凭想当然来实现。
3. LinkedList方法使用不当
在题目集6中,使用getFirst()时未检查队列是否为空,导致NoSuchElementException。例如:
// 危险写法
int floor = queue.getInternalRequests().getFirst();
// 安全写法
if(!queue.getInternalRequests().isEmpty()) {
int floor = queue.getInternalRequests().getFirst();
}
4. 电梯运行逻辑错误
- 没有完全理解电梯的运行方式,导致前两个题集都未能通过测试。
- 曾出现电梯在特定条件下"抖动"的问题——在两个楼层间反复移动。原因是方向判断逻辑有缺陷,未能正确处理所有边界情况。
调试方法:添加详细日志,打印每次移动前的队列状态和决策依据,最终定位到条件判断不完整的问题。
5. 重复请求处理错误
题目集6要求过滤连续重复请求,最初错误使用了AND逻辑:
// 错误逻辑
if(!input.equals(prevInput) && !queue.contains(request))
// 正确逻辑
if(!input.equals(prevInput))
发现过程:通过打印所有输入请求和实际加入队列的请求,发现某些合法请求被错误过滤。
6. 楼层数据混淆
题目集7中,由于外部请求格式变为<源,目的>,编码时多次混淆两者顺序。例如:
// 错误写法
Passenger p = new Passenger(destFloor, srcFloor);
// 正确写法
Passenger p = new Passenger(srcFloor, destFloor);
解决:为Passenger类属性使用明确命名,避免混淆。
四、改进建议
1. 类职责优化
当前Controller类仍承担了过多职责。可进一步拆分:
// 改进前
class Controller {
// 处理请求、调度电梯、管理状态、打印输出...
}
// 改进后
class Scheduler { // 专门处理调度逻辑
Direction determineDirection(...) {...}
}
class ElevatorOperator { // 管理电梯运行
void moveElevator(...) {...}
}
class RequestHandler { // 处理请求队列
void processRequests(...) {...}
}
2. 方法单一功能化
将复杂的processRequests方法拆分为多个单一功能方法:
// 改进前
void processRequests() {
// 50多行复杂逻辑
}
// 改进后
void processRequests() {
handleCurrentFloor();
determineNextAction();
executeMovement();
}
private void handleCurrentFloor() {...}
private void determineNextAction() {...}
private void executeMovement() {...}
3. 增强代码注释
为关键算法和复杂逻辑添加详细注释:
/
* 根据当前请求队列确定电梯运行方向
* 遵循LOOK算法原则:
* 1. 保持当前方向直到该方向无请求
* 2. 当该方向无请求时,检查反方向
* 3. 如果都无请求,进入IDLE状态
*/
private Direction determineDirection() {...}
4. 引入设计模式
考虑使用状态模式管理电梯状态:
interface ElevatorState {
void handleRequest(Controller c);
}
class MovingUpState implements ElevatorState {...}
class MovingDownState implements ElevatorState {...}
class IdleState implements ElevatorState {...}
5. 增强输入验证
添加更健壮的输入验证逻辑:
public static boolean isValidRequest(String input) {
// 验证格式
// 验证楼层范围
// 验证方向合理性(如1层不能DOWN)
}
五、总结
学习收获
通过这三次题目集的迭代开发,获得了以下提升:
-
面向对象设计能力:从最初的单一类设计,到逐步理解并应用单一职责原则,学会了如何合理划分类职责。
-
架构设计意识:认识到良好的架构设计对系统可维护性和可扩展性的重要性。
-
调试技巧:通过解决各种边界条件问题,掌握了有效的调试方法,如日志打印、单元测试等。
-
代码质量意识:开始关注代码复杂度、注释完整性等质量指标,而不仅仅是功能实现。
-
重构能力:学会了如何在不破坏现有功能的情况下改进代码结构。
待提升领域
- 理解能力:提升对题目要求的分析能力,读懂题目的目的,理解题目的内涵以圆满完成题目要求。
- 设计模式:需要进一步学习常用设计模式,如状态模式、策略模式等,以应对更复杂场景。
- 文档能力:需要养成及时编写文档和注释的习惯。
- 性能优化:当前的调度算法还有优化空间,可以研究更高效的实现方式。
课程建议
- 阶段性指导:在迭代题目之间提供一些设计指导,帮助逐步平稳过渡。
- 详细测试点:可以提供每个测试点的详细要求,更好定位错在哪,方便改进。
- 分值分配:建议迭代题目的分值递增设置,以鼓励同学尝试。
- 题后讲解:建议在每次发布题目集之后对上一个题目集进行讲解,或在班级群中发布上一个题目集的解析,这样可以帮助已完成的学生查找可改进之处、未完成的学生学习方法,让我们在完成之后可以学习借鉴并反思。就比如这次的电梯题目,很多同学上一个题目集都没能弄明白自己错在哪,下一个题目集又发布了,导致逐渐失去了希望和尝试的热情,也有许多完成了的同学不知道自己的程序是否还有改进之处,做对是否是误打误撞。
小结
这三次题目集构成了一个完整的阶梯式迭代过程,从基础实现到架构优化再到需求变更应对,这种渐进式的学习方式非常有效,我们也在不断的测试和改进中逐渐成长。虽然完成这一系列的题目有些“坐牢“,花费了许多时间和精力,但是这样的综合性程序是我们迟早要面对并且必须要掌握的。
浙公网安备 33010602011771号