电梯调度程序分析

前言

  这三次题目集围绕电梯调度程序展开,涉及了面向对象编程的多个重要知识点。在第一次题目中,主要聚焦于类的设计,涵盖了类的属性、方法的定义,以及如何通过类来模拟电梯的运行状态和处理请求。需要理解类的封装性,将电梯的各种属性(如最大楼层数、最小楼层数、当前楼层等)和行为(如移动、开门、关门等)封装在一个类中。
  第二次题目则着重于单一职责原则(SRP)的应用。通过拆分不同功能到不同的类中,如电梯类、乘客请求类、队列类和控制类,让每个类只负责一项明确的职责,提高了代码的可维护性和可扩展性。同时,还涉及到输入数据的处理,如过滤无效请求和重复请求。
  第三次题目进一步深化了面向对象设计,引入了乘客类,取消了乘客请求类,优化了类的设计。并且对外部请求的处理逻辑进行了调整,将请求目的楼层加入到内部请求队列。
  第一次题目是基础,主要是构建一个简单的电梯类,理解电梯的基本运行规则和请求处理逻辑,难度相对较低,是后续迭代的基础。
  第二次题目在第一次的基础上进行迭代,引入了单一职责原则,需要对代码进行重构,设计多个类并合理分配职责,难度有所提升。不仅要保证电梯的运行逻辑正确,还要处理无效请求和重复请求,对代码的健壮性提出了更高的要求。
  第三次题目再次迭代,引入新的类和修改请求处理逻辑,难度进一步加大。需要重新审视之前的设计,对代码进行调整和优化,确保新的功能能够正确实现,同时要保证原有功能不受影响。

设计与分析

第一次题目:单类设计电梯调度程序

设计与分析

类图分析

代码分析

在这个单类设计中,Elevator 类封装了电梯的所有属性和行为。minFloor 和 maxFloor 定义了电梯的运行范围,currentFloor 表示当前所在楼层,direction 记录电梯的运行方向, 
status 表示电梯的运行状态。internalRequests 存储电梯内部乘客的请求,externalUpRequests 和 externalDownRequests 分别存储电梯外部上行和下行的请求。
addInternalRequest 和 addExternalRequest 方法用于添加请求,同时会检查请求的楼层是否在有效范围内。run 方法是电梯的核心运行逻辑,它会不断检查请求队列,根据请求的 
方向和当前楼层决定电梯的移动方向,并处理相应的请求。

踩坑心得

数据输入处理

格式错误处理:输入格式多样且可能不规范,比如电梯内乘客请求格式<楼层数>、电梯外乘客请求格式<乘客所在楼层数,乘梯方向> ,用户可能会输入错误格式,像楼层数与方向之 
间的逗号缺失、楼层数不是数字等情况。若程序未对这些错误格式进行处理,可能导致程序运行时抛出异常而崩溃。
无效楼层处理:需要判断输入的楼层数是否在电梯的有效楼层范围内(最小楼层数到最大楼层数 )。若用户输入超出范围的楼层数,未做处理的话,会使电梯运行逻辑混乱,例如 
电梯往不存在的楼层运行等不合理情况。
结束标识处理:以 “end”(不区分大小写)作为输入结束标识,要准确识别并停止接收输入。若判断逻辑有误,可能导致程序一直等待输入,无法进入后续电梯运行模拟环节。

电梯状态与方向管理

初始状态设置:电梯默认停留在 1 层,状态为静止。若初始状态设置错误,比如当前楼层设置错误或运行方向设置错误,会使后续电梯运行逻辑从一开始就出现偏差。
状态切换逻辑:电梯运行过程中有停止、移动中、开门、关门等状态。在状态切换时,逻辑容易出错,比如电梯在移动中不应该开门,但如果逻辑判断失误,可能会出现不合理的状 
态切换。
方向确定逻辑:当有乘客请求时确定电梯运行方向,若电梯初始静止且同时有多个不同方向的请求,如何确定优先方向需要清晰的逻辑。如果处理不当,可能导致电梯运行混乱,没 
有按照预期规则优先处理同方向请求。

请求队列管理

内部请求队列:要确保内部乘客请求能正确添加到队列中,且不能重复添加(同一楼层的重复请求应忽略 )。若重复添加判断逻辑有误,会导致电梯不必要地多次停靠同一楼层。
外部请求队列:外部请求要区分上行和下行队列,添加请求时要保证请求正确归类到对应的队列。如果归类错误,比如将下行请求误放到上行队列,会使电梯无法按照正确顺序处理 
请求。
请求处理顺序:电梯运行时,要按照规则优先处理同方向请求,处理完同方向请求后再处理反方向请求。若请求处理顺序逻辑混乱,比如不区分方向随意处理请求,就不符合电梯运 
行规则。

电梯运行逻辑

楼层移动逻辑:电梯每次移动一个楼层,要准确判断是否到达目标楼层。若判断逻辑错误,可能导致电梯多走或少走楼层。
停靠判断逻辑:到达某一楼层时,要检查是否有需要停靠的请求(包括电梯内和电梯外 )。若检查逻辑不全面,可能会错过一些请求,导致乘客无法正常乘坐电梯。

改进建议

1、输入处理优化
增强输入格式验证
编写更完善的正则表达式或验证函数,对用户输入的格式进行严格检查。例如,对于电梯内请求<楼层数>,确保输入是一个有效的整数且符合楼层范围;对于电梯外请求<乘客所在楼层数,乘梯方向>,验证逗号分隔是否正确,方向是否为 “UP” 或 “DOWN”。
当输入格式错误时,给出明确的错误提示,引导用户重新输入正确格式的数据
处理边界情况和异常输入
除了验证楼层范围,还要考虑一些特殊情况,如输入为负数、零(如果最小楼层是 1)等。
对于非预期的输入,如字母、符号等,要进行捕获和处理,避免程序崩溃。
2、方向决策优化
当电梯处于静止状态且有多个不同方向的请求时,采用更合理的策略来确定优先方向。例如,可以根据距离电梯当前楼层最近的请求方向作为优先方向。
实现一个函数来计算每个请求与当前楼层的距离,然后选择距离最近的请求方向。
3、减少不必要的计算
在电梯运行过程中,避免重复计算一些数据,例如每次判断是否需要停靠时,不要重复遍历请求队列,可以将一些中间结果进行缓存。
例如,在电梯到达某一楼层时,提前计算好该楼层是否有请求,避免每次都重新遍历队列。

第二次题目:单部电梯调度程序(类设计)

设计与分析

类图分析

代码分析

第二次迭代的核心目标是遵循单一职责原则(SRP),将第一次迭代中的 “万能电梯类” 拆分为多个职责明确的类,解决 “类职责过多” 的问题。以下是关键类的设计与职责:

  1. 乘客请求类(PassengerRequest)
    职责:
    专门封装乘客请求数据,区分电梯内请求(目标楼层)和电梯外请求(所在楼层 + 方向)。
    设计要点:
    外部请求包含楼层和方向(UP/DOWN),内部请求仅包含目标楼层(方向由电梯当前位置决定)。
    屏蔽无效数据细节,例如在构造方法中过滤非法楼层(需配合控制类实现)。
    作用:
    解耦请求数据与业务逻辑,使请求数据的传递和处理更清晰。
  2. 请求队列类(RequestQueue)
    职责:
    管理电梯内 / 外请求队列,实现请求的添加、去重、查询。
    设计要点:
    使用三个独立列表分别存储内部请求(internalRequests)、外部上行请求(externalUpRequests)、外部下行请求(externalDownRequests)。
    添加请求时自动去重(例如,同一楼层的外部上行请求重复提交时仅保留一个)。
    核心方法:
    addInternalRequest():添加内部请求,自动过滤重复楼层。
    addExternalRequest():根据方向将请求归类到上行 / 下行队列,并去重。
    作用:
    分离请求管理逻辑,使电梯类和控制类无需关心队列细节,仅通过接口操作队列。
  3. 电梯类(Elevator)
    职责:
    管理电梯的物理状态(当前楼层、运行方向、状态)和动作(移动、开关门),不包含任何调度逻辑。
    设计要点:
    状态属性:currentFloor(当前楼层)、direction(上行 / 下行 / 静止)、status(停止 / 移动中 / 开门 / 关门)。
    动作方法:moveUp()/moveDown()(改变楼层并输出移动日志)、openDoor()/closeDoor()(输出开关门日志)。
    作用:
    纯数据与动作封装,符合 “电梯只负责物理运行” 的职责,调度逻辑由控制类负责。
  4. 控制类(ElevatorController)
    职责:
    核心调度逻辑,负责输入解析→请求分配→方向决策→请求处理的完整流程。
    设计要点:
    输入解析:将用户输入(如<3>或<5,UP>)转换为PassengerRequest对象,并过滤无效请求(如楼层超范围、方向错误)。
    方向决策:根据当前电梯位置和请求队列,确定运行方向(优先处理同方向请求)。
    请求处理循环:按 “同方向优先” 规则,循环处理当前方向上的所有请求,直至无同方向请求时切换方向。
    核心方法:
    processRequest(String input):解析输入并添加到请求队列。
    runElevator():主循环逻辑,控制电梯移动和停靠。
    determineDirection():私有方法,确定电梯初始运行方向(静止时触发)。
    作用:
    集中调度逻辑,确保电梯按规则处理请求,符合 “控制类只负责调度” 的职责。

踩坑心得:第二次迭代常见问题与解决方案

  1. 类职责划分不彻底,残留 “混合逻辑”
    问题描述:
    误将请求去重逻辑放入Elevator类,导致Elevator承担 “状态管理 + 请求管理” 双重职责。
    在RequestQueue中混入调度逻辑(如判断是否需要改变方向),违反单一职责原则。
    解决方案:
    严格遵循 “每个类只做一件事”:
    RequestQueue只负责请求的存储、去重、查询,不涉及调度逻辑。
    ElevatorController负责所有调度决策(方向、停靠逻辑)。
  2. 请求去重逻辑错误,导致重复处理
    问题描述:
    使用List.contains()判断重复请求时,未正确重写PassengerRequest的equals()和hashCode()方法,导致相同请求被重复添加。
    例如:两个楼层和方向相同的PassengerRequest对象,因未重写equals()被视为不同请求。
    解决方案:
    在PassengerRequest类中重写equals()和hashCode(),基于楼层和方向判断是否相等。
  3. 方向判断逻辑混乱,未优先处理同方向请求
    问题描述:
    电梯移动时未严格遵循 “同方向请求优先” 规则,例如在向上运行时处理了下行请求。
    方向切换条件错误(如未处理完当前方向所有请求就切换方向)。
    解决方案:
    在ElevatorController中添加handleSameDirectionRequests()方法,循环处理当前方向上的所有请求(包括内部请求和外部同方向请求)。
  4. 输入处理不严谨,未过滤无效请求
    问题描述:
    用户输入非法楼层(如负数、超过最大楼层)或错误方向(如 “Up” 小写)时,程序崩溃或逻辑混乱。
    解决方案:
    在ElevatorController.processRequest()中添加输入验证:
    使用正则表达式校验输入格式(如内部请求为<数字>,外部请求为<数字,UP/DOWN>)。
    校验楼层范围(是否在minFloor和maxFloor之间)。
  5. 测试覆盖不全面,边缘情况未验证
    问题描述:
    未测试 “电梯静止时同时有上下行请求”“请求楼层等于当前楼层” 等边缘情况,导致调度逻辑错误。
    解决方案:
    设计测试用例覆盖以下场景:
    电梯静止时,先有上行请求,再有下行请求,验证是否优先处理上行。
    电梯运行中,同方向新增请求,验证是否顺路处理。
    请求楼层等于当前楼层,验证是否不重复停靠。
    使用单元测试框架(如 JUnit)自动化验证逻辑。

改进建议

优化请求队列数据结构
现状问题:
使用List存储请求队列,查找和删除操作效率较低。
改进方案:
对内部请求队列按楼层排序(如使用TreeSet),便于按顺序处理。
对外部请求队列使用HashMap按楼层分组,快速判断某楼层是否有请求。

第三次题目:单部电梯调度程序(类设计-迭代)

设计与分析

类图分析

代码分析

设计思路
第三次迭代在第二次迭代遵循单一职责原则的基础上,引入了 Passenger 类,进一步细化了模型,使代码更贴合实际场景。原本在第二次迭代中使用的 PassengerRequest 类主要聚焦于请求信息,而此次新增的 Passenger 类封装了乘客的起始楼层和目标楼层,让程序能更好地模拟乘客的行为。同时,对请求处理逻辑进行了相应调整,以适应新的类结构。
各主要类的职责
Passenger 类
封装了乘客的起始楼层 sourceFloor 和目标楼层 destinationFloor。这两个属性明确了乘客的出行需求,为后续的请求处理和电梯调度提供了关键信息。
提供了获取起始楼层和目标楼层的方法,方便其他类使用这些信息。
RequestQueue 类
管理电梯内、外的乘客请求队列。使用 List 分别存储内部请求 internalRequests 和外部请求 externalRequests。
提供了添加请求和获取请求队列的方法,以及检查是否还有未处理请求的方法。
Elevator 类
负责管理电梯的物理状态,包括最小楼层、最大楼层、当前楼层、运行方向和运行状态。
提供了电梯移动、开门和关门等操作的方法,这些方法是电梯实际运行的基础。
ElevatorController 类
作为核心控制类,负责处理乘客请求和控制电梯的运行。
包含了确定电梯运行方向和处理同方向请求的逻辑,是整个电梯调度系统的核心。

踩坑心得

  1. 类之间的交互问题
    问题描述:引入 Passenger 类后,类之间的交互变得更加复杂。例如,在 ElevatorController 处理请求时,需要同时考虑 Passenger 的起始楼层和目标楼层,可能会出现逻辑混乱。如果在调用 RequestQueue 的方法时,没有正确区分内部请求和外部请求,可能会导致请求处理错误。
    解决方案:在编写代码时,要清晰地定义每个类的接口和交互方式。在 ElevatorController 中,对内部请求和外部请求的处理逻辑进行明确区分,添加详细的注释,提高代码的可读性和可维护性。
  2. 请求处理逻辑的调整
    问题描述:由于引入了新的 Passenger 类,原有的请求处理逻辑需要进行调整。如果没有充分考虑到新类的影响,可能会导致电梯调度出现问题。例如,在确定电梯运行方向时,没有正确考虑乘客的起始楼层和目标楼层,可能会使电梯做出不合理的运行决策。
    解决方案:对原有的请求处理逻辑进行全面审查,根据 Passenger 类的特点进行调整。在确定电梯运行方向时,综合考虑乘客的起始楼层和目标楼层,制定合理的调度策略。
  3. 数据一致性问题
    问题描述:在多线程环境下,RequestQueue 中的请求队列可能会被多个线程同时访问和修改,导致数据不一致。例如,一个线程正在添加请求,另一个线程同时在处理请求,可能会出现数据冲突。
    解决方案:使用同步机制来保证数据的一致性。可以使用 synchronized 关键字对 RequestQueue 中的关键方法进行同步,确保在同一时间只有一个线程可以访问和修改请求队列。

改进建议

优化请求队列的管理
现状问题:目前使用 List 存储请求队列,在查找和删除请求时效率较低。
改进方案:可以考虑使用更高效的数据结构,如 TreeSet 或 HashMap。如果使用 TreeSet,可以根据乘客的起始楼层或目标楼层进行排序,方便快速查找和处理请求。如果使用 HashMap,可以根据楼层作为键,将乘客请求作为值存储,提高查找效率。

总结

收获

通过这三次题目集的练习,我对面向对象编程有了更深入的理解。学会了如何设计类,合理分配类的职责,遵循单一职责原则提高代码的可维护性和可扩展性。同时,也掌握了如何处理输入数据、管理请求队列和实现调度算法等实际开发中常见的问题。

需要进一步学习和研究的方向

虽然已经完成了电梯调度程序的设计,但仍有很多方面需要进一步学习和研究。例如,如何优化调度算法,提高电梯的运行效率;如何处理并发请求,使程序能够处理多个乘客同时请求的情况;如何进行性能测试和优化,确保程序在大规模数据下的稳定性和性能。

posted @ 2025-04-20 22:42  星澜fhv  阅读(62)  评论(0)    收藏  举报