电梯调度程序阶段性总结:控制类算法之难

电梯调度程序阶段性总结:控制类算法之难
前言
题目集 5 - 7 围绕电梯调度程序逐步展开,从基础的电梯类搭建到遵循设计原则进行迭代优化,每一步都充满挑战,题目集 5 开篇,引导构建起基本的电梯类,涵盖电梯的核心属性与运行逻辑;题目集 6 引入了单一职责原则这一重要的设计理念,要求对代码结构进行优化和拆分,提升了代码的可维护性与可读性,但同时也对类设计能力提出了更高挑战。到了题目集 7,进一步对乘客类等进行细化设计,在延续之前规则的基础上,对请求处理等逻辑进行了调整。控制类作为电梯调度的核心枢纽,其算法设计的复杂性贯穿其中,是整个程序实现过程中的关键难点。
设计与分析
题目集 5:控制类算法雏形与困境
在题目集 5 中,虽然尚未明确将控制逻辑独立成类,但电梯类实际上承担了部分控制功能,当时设计的电梯类(Elevator)几乎承载了所有的功能与属性。它包含了电梯的最大楼层数(maxFloor)、最小楼层数(minFloor,默认设为 1)、当前楼层(currentFloor)、运行方向(direction)、运行状态(state),以及电梯内部乘客的请求队列(internalRequests)和电梯外部楼层乘客的请求队列(externalRequests )。此时的电梯调度逻辑相对简单直接,根据接收到的乘客请求,判断电梯当前运行方向,优先处理同方向请求。然而,这种简单的设计背后却隐藏着诸多问题。

当请求数量较少时,这种逻辑看似能够正常运行。但随着请求增多,由于电梯类承担了过多的职责,导致代码耦合度极高,因此一旦需要对电梯的某个功能进行修改或扩展,可能会影响到其他功能的正常运行。例如,在判断电梯是否应该改变运行方向时,由于没有清晰的算法结构,需要在多个方法中重复编写条件判断代码,一旦需求发生变化,如增加新的请求类型或特殊运行规则,修改代码就变得异常困难,牵一发而动全身,极易引发新的错误。而且,这种混合式的代码结构使得程序的可读性和可维护性极差,难以理解和扩展。
class Elevator {
int currentFloor;
String direction;
List innerRequests = new ArrayList<>();
Map<Integer, List> outerRequests = new HashMap<>();
// ...
}
电梯类承担了请求存储、调度逻辑、状态管理等多个职责

public void move() {
while (!innerRequests.isEmpty() || !outerRequests.isEmpty()) {
if (direction.equals("UP")) {
currentFloor++;
} else {
currentFloor--;
}
// 检查当前楼层请求...
}
}
未处理反向请求的优先级,导致样例1中6层DOWN请求未被及时响应
题目集 6:控制类独立后的算法优化
到了题目集 6,开始遵循单一职责原则对电梯程序进行迭代型设计,从类图可以清晰的看到控制类(Controller)被独立出来,专门负责电梯调度过程。这一改变在理论上让代码结构更加清晰,程序被拆分为电梯类(Elevator)、乘客请求类(ExternalRequest )、队列类(RequestQueue)和控制类(Controller)。电梯类(Elevator)主要负责管理自身的状态,如当前楼层、运行方向和运行状态等;乘客请求类(ExternalRequest )则专注于封装乘客的请求信息,包括乘客所在楼层(floor)和乘梯方向(direction);队列类(RequestQueue)负责管理乘客的请求队列,包括内部请求队列(internalRequests)和外部请求队列(externalRequests);控制类(Controller)则承担起电梯调度的核心职责。

从类图可知,控制类需要与电梯类(Elevator)、队列类(RequestQueue)紧密协作。在确定电梯运行方向时,控制类需要综合考虑电梯当前位置、运行状态以及请求队列中的请求信息。在电梯调度程序中,各个类就如同不同部门在协同工作。请求队列类负责收集乘客的请求信息,而控制类则根据这些信息来指挥电梯的运行。当有新的乘客请求产生并加入到请求队列类时,控制类需要立刻获取到这个信息,以便及时调整电梯的调度策略。然而,由于类与类之间用于数据交互的接口设计存在缺陷,就像一条有故障的信息传输通道。这会导致新请求的信息无法及时、准确地传递给控制类。控制类因为得不到最新、最准确的请求情况,在做出电梯运行方向、停靠楼层等关键调度决策时,就如同在缺少重要情报的情况下指挥作战,很容易做出错误的判断,进而影响整个电梯调度系统的正常运行。

这种设计模式相较于题目集 5 有了显著的进步。各模块的职责更加明确,代码的可读性和可维护性得到了大幅提升。例如,当需要处理无效楼层请求或重复请求时,可以在相应的类中进行针对性的处理。在队列类中,可以添加逻辑来过滤掉无效楼层请求和重复请求,而不会影响到电梯类和控制类的正常功能。然而,在实际开发过程中,类之间的协作还存在一些不够流畅的地方。比如,控制类在获取和处理请求队列信息时,与队列类之间的接口调用略显繁琐,这在一定程度上影响了代码的执行效率。
题目集 7:控制类算法的深化与困扰
题目集 7 在题目集 6 的基础上进一步迭代,加入乘客类(Passenger)并修改外部请求格式,从新的类图可以看到,乘客类(Passenger)封装了乘客的源楼层(sourceFloor)和目的楼层(destinationFloor)信息。

新的请求格式要求控制类在处理外部请求时,不仅要关注请求源楼层和目的楼层,还需要在处理完外部请求后,将目的楼层加入到内部请求队列。这使得控制类的请求处理逻辑变得更加复杂。在调度算法中,需要重新规划电梯的停靠策略和运行路径。例如,当电梯处理完一个外部请求到达目的楼层后,需要根据新加入内部队列的请求以及原有内部请求,重新评估运行方向和下一个停靠楼层。这涉及到对多个请求队列的动态分析和决策,算法的逻辑深度和广度都远超之前。同时,由于请求格式和处理规则的变化,原有的测试用例无法完全覆盖新的情况,需要重新设计和编写大量测试用例来验证控制类算法的正确性。而在调试过程中,由于算法涉及多个类之间的交互和复杂的逻辑判断,很难快速定位到问题根源,往往需要花费大量时间进行排查和修复。
采坑心得
在处理无效楼层请求时,我在题目集 5 的代码中就遭遇了滑铁卢。当时,由于没有对输入的楼层进行严格的有效性检查,当用户输入超过最大楼层数或低于最小楼层数的无效楼层时,程序就会出现异常。为了解决这个问题,我在电梯类中添加了isValidFloor(int floor)方法,用于判断输入楼层是否在有效范围内。在每次接收到乘客请求时,都会先调用该方法进行检查。通过使用边界值测试,如输入最小楼层 - 1、最大楼层 + 1 等特殊值,我确保了这个判断逻辑的准确性。
而在处理重复请求方面,我也经历了一番波折。在题目集 6 中,最初我只是简单地比较相邻的请求是否相同,来判断是否为重复请求。但在实际测试中,我发现对于间隔出现的相同请求,这种方法无法有效过滤。于是,我重新调整了逻辑,在队列类中遍历整个请求队列,检查是否存在重复的请求。利用LinkedList数据结构的特性,方便地进行插入和删除操作,有效地解决了重复请求的问题。经过大量测试数据的验证,这一改进确保了程序能够正确处理各种重复请求的情况。
在题目集 6 的编写过程中,我深刻体会到了类设计不合理带来的困扰。尽管遵循了单一职责原则对代码进行了拆分,但在实际编写过程中,控制类的代码量还是迅速膨胀起来。它承担了过多本应由其他类负责的逻辑,导致代码结构变得混乱。例如,在处理电梯调度逻辑时,控制类不仅要负责决定电梯的运行方向和停靠楼层,还要处理一些与电梯状态更新相关的细节,这使得控制类的代码变得冗长且难以理解。
这次经历让我反思自己在开发过程中的不足。在一开始编写代码时,我过于急于实现功能,而忽略了对整体架构和逻辑的深入思考。导致在后续的开发和维护过程中,不断出现各种问题。这让我深刻认识到,在软件开发中,前期的设计和规划是至关重要的,只有打好基础,才能事半功倍。

正则表达式匹配失败
Pattern.compile("<(\d+),(UP|DOWN)>"); // 未处理首尾空格
修正为\s<(\d+)\s,\s(UP|DOWN)>\s

请求去重逻辑错误
public void add(Request req) {
if (requests.contains(req)) return; // 错误:仅判断对象相等
}
重写equals()方法基于楼层和类型比较
改进建议
类设计优化
优化接口设计:重新审视类之间的接口,确保数据传递简洁明了。减少不必要的参数传递和复杂的接口调用方式,使类之间的协作更加流畅。例如,在控制类与电梯类、队列类之间的交互中,可以通过设计更合理的接口,减少控制类对其他类内部细节的依赖,降低类之间的耦合度。
引入设计模式:深入学习并尝试运用设计模式来优化代码结构。例如,对于电梯调度算法,可以考虑使用策略模式,将不同的调度策略封装成独立的策略类。这样,在需要更换或扩展调度算法时,只需要创建新的策略类,而不需要对核心的电梯调度逻辑进行大规模修改,提高了代码的灵活性和可扩展性。
算法优化
分层设计:将控制类算法按照功能划分为不同层次,如请求接收层、调度决策层和执行控制层。请求接收层专门负责接收和整理来自不同队列的请求信息;调度决策层基于请求信息和电梯状态进行运行方向、停靠楼层等决策;执行控制层则负责将决策转化为对电梯类的具体操作指令。通过分层设计,降低算法的复杂性,提高代码的可读性和可维护性。
引入启发式算法:对于请求优先级的判断,可以引入启发式算法,如基于距离和时间的综合评估算法。不再单纯依赖楼层距离或请求先后顺序,而是综合考虑电梯当前速度、预计到达时间等因素,更智能地确定请求优先级,提高电梯运行效率。
总结
通过这三次题目集的实践,我在多个方面都取得了显著的进步,从最初的逻辑混乱到逐步梳理清晰,每一步都经历了无数次的尝试和错误。在知识层面,我对面向对象编程的理解更加深入,熟练掌握了类的设计、封装、继承和多态的应用。学会了如何根据实际需求合理地设计类的属性和方法,以及如何通过类之间的协作来实现复杂的算法逻辑。通过这一系列的实践,深刻认识到算法设计不仅要考虑功能实现,更要注重代码结构的合理性、数据的一致性以及边界条件的处理。
尽管取得了一定的进步,但我也清楚地认识到自己还存在很多不足之处。在类设计的合理性方面,虽然已经在努力遵循设计原则,但在实际应用中,还不能总是准确地判断类的职责边界,导致有时会出现职责划分不够清晰的情况。在代码优化方面,对于算法的时间复杂度和空间复杂度的分析还不够深入,导致在处理一些复杂问题时,代码的性能可能不是最优的。
通过对题目集 5 - 7 的总结和反思,我对自己的学习情况有了更清晰的认识,也明确了未来的努力方向。相信在不断的总结和实践中,我能够在软件开发的道路上稳步前进。

posted @ 2025-04-20 18:25  阿那克萨戈拉斯  阅读(123)  评论(0)    收藏  举报