NCHU_单部电梯调度

(1)前言:
第一次题目集:基础功能实现
​核心任务​:使用单个电梯类完成所有功能,包括状态管理、请求队列管理和调度算法
​知识点​:
​面向对象基础​:类的定义(属性、方法)、封装
​数据结构和算法​:使用集合(如LinkedList)模拟内部和外部请求队列;实现“同方向优先”的扫描(SCAN)调度算法
​程序逻辑与控制流​:复杂的条件判断(决定电梯移动方向、是否停靠)、循环控制(电梯运行循环)、输入输出处理
​异常处理​:对无效楼层请求的识别和忽略
​题量与难度​:
为1道编程大题,但需要通过的测试用例较多,覆盖各种边界情况(如最高/最低楼层、连续相同请求、方向判断等)
​难度中等,难点在于将所有逻辑(电梯运行规则、队列管理、输入输出)正确地整合在一个类中,代码容易变得冗长且结构不清晰
第二次题目集:遵循单一职责原则的重构
​核心任务​:将“上帝类”(职责过多的电梯类)拆分为多个各司其职的类。
​知识点​:
​软件设计原则​:​单一职责原则​ 是本次的核心考点。理解并实践“一个类应该只有一个引起变化的原因”
​类图设计​:根据UML类图进行开发,理解类之间的关系(关联、依赖)
​类的设计与拆分​:典型地拆分为:
Elevator:只关心自身状态(当前楼层、方向、状态)
PassengerRequest(或Request):封装一条请求的信息
RequestQueue:专门负责管理内部和外部请求队列的增删改查
Controller:核心调度器,持有电梯和队列的引用,实现调度算法,控制电梯运行
​题量与难度​:
​1道重构题。在第一次题目集代码的基础上进行重构,而非从零开始
​难度中等偏难,难点在于如何合理地划分职责,并确保拆分后的类能良好协作。对学生的抽象能力和设计能力要求更高
第三次题目集:引入乘客类与逻辑变更
​核心任务​:再次调整设计,用Passenger类替代PassengerRequest类,并改变外部请求的输入和处理逻辑
​知识点​:
​迭代开发与重构​:应对需求变更的能力。当需求变化(如外部请求格式从<楼层, 方向>变为<源楼层, 目的楼层>)时,如何调整现有设计
​更深层次的抽象​:引入Passenger概念,使其包含sourceFloor(源楼层)和destinationFloor(目标楼层)。这使得“请求”不再是单一楼层,而是一个有起点和终点的完整旅程
​复杂的逻辑处理​:新的规则要求,处理一个外部请求后,需要将其目的楼层自动加入内部请求队列。这增加了调度逻辑的复杂度
​巩固类间协作​:Controller类需要协调Elevator, Passenger, RequestQueue之间的关系,设计更为精细
​题量与难度​:
​1道重构题。在第二次题目集的设计基础上进行修改
​难度为难,最大的挑战在于理解新规则并无缝地融入到已有架构中,同时保持代码的清晰和可维护性。它考察的是学生对面向对象设计的整体把握能力和解决复杂问题的灵活性
(2)设计与分析
题目集2-3围绕单部电梯调度程序展开,通过两次迭代逐步优化设计。题目集5采用单类实现基础功能,题目集6则遵循单一职责原则进行类拆分重构。本总结将基于SourceMonitor报表和PowerDesigner类图,深入分析两次题目集的设计演进、代码质量及改进方向
题目集2采用单类Elevator实现所有功能,包含状态管理、请求队列管理和调度算法。使用数组存储内部请求和外部请求队列,通过int类型标识方向(1-上行,2-下行)
SourceMonitor分析
​代码质量指标​:
代码行数:200-300行
最大复杂度:24(集中在runLift方法)
平均方法长度:8.29行
分支语句占比:30.6%
注释比例:极低(约0.4%)
​主要问题​:
​方法嵌套深度达6层​:runLift方法内部包含多层if-else嵌套,可读性极差
​数组管理繁琐​:使用int数组存储请求队列,容易出现索引越界异常
​调度逻辑与状态管理混杂​:同一个方法既要处理电梯移动,又要管理请求队列
​方向判断复杂​:使用int类型判断方向,代码中频繁出现硬编码判断
类图特征
采用单一Elevator类,包含所有属性和方法:
属性:currentFloor(当前楼层)、direction(方向)、internalRequests(内部请求数组)、externalRequests(外部请求数组)
方法:runLift()(核心调度逻辑)、move()(电梯移动)、shouldStop()(停靠判断)
题目集3将单类拆分为多个各司其职的类:
Elevator:只关心自身状态(当前楼层、方向、上下限)
RequestQueue:管理内部请求和外部请求队列
Controller:核心调度器,实现调度算法
Request:封装请求信息
SourceMonitor分析
​代码质量指标​:
代码行数:300-450行
最大复杂度:降至8
平均方法长度:4.18行
分支语句占比:21.1%
方法调用次数:95次(跨类调用频繁)
​设计优势​:
​职责分离​:每个类功能明确,Elevator只关心物理状态,RequestQueue管理队列,Controller负责调度
​数据结构优化​:使用LinkedList替代数组,便于动态增删请求
​可读性提升​:引入枚举类型标识方向,代码更易理解
类图特征
​类间关系​:
Controller持有Elevator和RequestQueue的引用
RequestQueue包含内部请求队列和外部请求队列
Request类封装楼层和方向信息
​设计模式应用​:
单一职责原则:每个类只有一个引起变化的原因
依赖注入:Controller通过构造函数注入依赖对象
(3)踩坑心得:
第一次代码:单类基础实现
​核心问题​:将所有功能集中在Elevator类中,违反单一职责原则。
​SourceMonitor分析结果​:
代码行数:约200-300行
最大复杂度:24(集中在runLift方法)
平均方法长度:8.29行
分支语句占比:30.6%
注释比例:极低(约0.4%)
​踩坑点​:
​方法嵌套深度达6层​:runLift方法内部包含多层if-else嵌套,可读性极差
​数组管理繁琐​:使用int数组存储请求队列,容易出现索引越界异常
​调度逻辑与状态管理混杂​:同一个方法既要处理电梯移动,又要管理请求队列
​方向判断复杂​:使用int类型(1-上行,2-下行)判断方向,代码中频繁出现硬编码判断
​PTA测试结果​:通过率约70%,主要失分点在于边界条件处理不当和死循环问题
第二次代码:按SRP原则拆分类
​核心改进​:将单类拆分为Elevator、RequestQueue、Controller、Request等多个类
​SourceMonitor分析结果​:
代码行数:约300-450行
最大复杂度:降至8
平均方法长度:4.18行
分支语句占比:21.1%
方法调用次数:95次(跨类调用频繁)
​设计优势​:
​职责分离​:每个类功能明确,Elevator只关心物理状态,RequestQueue管理队列,Controller负责调度
​数据结构优化​:使用LinkedList替代数组,便于动态增删请求
​可读性提升​:引入枚举类型标识方向,代码更易理解
​踩坑点​:
​类间耦合度过高​:Controller类需要同时持有Elevator和RequestQueue的引用,耦合度依然较高
​请求处理逻辑分散​:外部请求和内部请求的处理逻辑分散在不同方法中
​测试用例覆盖不全​:重构后部分边界测试用例未通过,需要重新调试
​PTA测试结果​:通过率提升至85%,主要失分点在于复杂场景下的调度逻辑错误
第三次实验:引入乘客类与请求重构
​核心改进​:引入Passenger类,重构请求处理流程
​SourceMonitor分析结果​:
代码行数:约400-530行
最大复杂度:降至4-6
平均方法长度:3.45-5.12行
分支语句占比:17.6-23.2%
类数量:12个(需考虑分包)
​设计优势​:
​请求处理更贴近现实​:外部请求格式改为"<源楼层,目的楼层>",停靠源楼层后自动将目的楼层加入内部队列
​调度逻辑简化​:核心算法控制在15行以内
​性能提升​:PTA用时缩短至128ms
​可维护性显著提升​:代码结构清晰,便于扩展
​踩坑点​:
​初始设计过度复杂​:第一次设计时尝试使用策略模式,导致类数量过多(达15个),反而增加了复杂度
​请求转换逻辑错误​:外部请求转换为内部请求时,未正确处理方向判断
​并发问题​:多线程环境下请求队列的线程安全问题未考虑
​内存泄漏​:未及时清理已完成的请求对象
​PTA测试结果​:通过率约65%
(4)改进建议
1.​问题​:使用LinkedList虽然便于增删,但查找效率较低。
​改进方案​:
针对频繁查找的场景,使用PriorityQueue或TreeSet
针对楼层判断,使用BitSet标记需要停靠的楼层
2.问题​:缺乏单元测试,重构时容易引入回归问题。
​改进方案​:
使用JUnit编写单元测试,覆盖核心方法
使用Mockito模拟依赖对象
目标测试覆盖率:70%以上
(5)总结:

  1. 面向对象设计能力显著提升
    通过三次迭代开发,深刻理解了单一职责原则​(SRP)的重要性。从题目集5的单类"上帝类"到题目集6的多类拆分,再到题目集7的进一步优化,学会了如何将复杂系统分解为职责单一、高内聚低耦合的组件。这种设计思维不仅适用于电梯调度程序,更是所有软件系统设计的基础
  2. 代码重构与演进能力
    三次题目集的核心价值在于渐进式重构。学会了在不破坏现有功能的前提下,通过小步快跑的方式持续改进代码质量。掌握了提取方法、提取类、引入接口等重构技巧,能够识别代码坏味道(如过长方法、过大类、重复代码)并进行有效重构
  3. 算法实现与优化能力
    深入理解了SCAN电梯调度算法的核心思想,掌握了同方向优先、边界判断、方向反转等关键逻辑。通过性能对比分析,认识到数据结构选择(数组 vs LinkedList)和算法复杂度对系统性能的影响,培养了性能优化的意识
  4. 工程化实践能力
    学会了使用SourceMonitor进行代码复杂度分析,用数据说话而不是凭感觉判断代码质量。掌握了基本的单元测试编写方法,理解了测试驱动开发(TDD)的价值。通过PTA平台的自动化测试,培养了持续集成和持续交付的工程思维
  5. 问题定位与调试能力
    在解决死循环、数组越界、调度逻辑错误等问题的过程中,掌握了系统化调试的方法:日志输出、断点调试、边界条件测试、异常堆栈分析等。学会了如何从测试用例失败信息中快速定位问题根源
posted @ 2025-11-22 23:19  -周鸿宇  阅读(0)  评论(0)    收藏  举报