电梯迭代Blog

单部电梯调度总结性blog

一、前言

本阶段三次题目集主要围绕面向对象编程和电梯调度算法展开。

感受:

三次迭代题目难度逐渐上升,但是,核心算法为一次性将所有请求加入队列,队列分为两个队列,外部队列和内部队列,内部队列只有楼层数,外部队列既有楼层数也有方向,处理请求时只对两个队列中第一个请求做处理,当同方向无请求可处理时,改变方向。

知识点:

队列

队列就像排队买奶茶的队伍,遵循"先进先出"原则,先来的人先买到奶茶。在Java中可以用Queue接口来实现。ArrayList是Java中最常用的动态数组,它比普通数组更灵活:

1、自动扩容:不用手动设置大小,添加元素时会自动变大
2、方便操作:提供add(),get(),remove()等简单方法
3、快速访问:可以通过下标直接获取元素
4、简单示例:
创建:ArrayList list = new ArrayList<>();
添加:list.add("苹果");
获取:String fruit = list.get(0);
删除:list.remove(0);
5、ArrayList比普通数组好在:
不用提前声明大小;增删元素更方便;自带很多实用方法

正则表达式

// 内部请求格式:<楼层号> 例如 <5>
String internalPattern = "<\d+>";

// 外部请求格式:<楼层号,方向> 例如 <4,UP> 或 <5,DOWN>
String externalPattern = "<\d+,\s*(UP|DOWN)>";

// 测试示例
String test1 = "<5>"; // 有效的内部请求
String test2 = "<2,UP>"; // 有效的外部请求
String test3 = "<4,DOWN>"; // 有效的外部请求
String test4 = "<a,UP>"; // 无效请求
String test5 = "<6,LEFT>"; // 无效请求

正则表达式很好的解决了在输入格式上面的限制,确保输入格式正确,从而正确的进行下面的操作,对于电梯程序可以保证将楼层数与方向分割开来并正确读取。

类与对象

类:就像设计图纸,描述一类事物的共同特征和行为。比如"电梯设计图"规定了电梯应该有哪些功能(上下移动、开关门等)。
对象:按照图纸造出来的具体东西。比如根据"电梯设计图"造出来的"1号电梯"就是一个实际可用的对象。

在编写程序之前,必须根据类图弄清楚需要编写那些类,类与类之间的关系又是怎样的,还要搞清楚需要new出哪些对象,对象之间有什么关系,类中又有哪些方法,方法又被哪个对象给调用了,根据类图很快能搞清楚这些逻辑关系,可见类图的作用是十分重要的,题集中有参考类图,所有可以直接根据参考的类图进行编写,但是如果在实际开发当中。我们需要自己设计类图,这时候我们就不能马虎,类图是一个程序的核心,有了清晰的类图编写程序会变得如鱼得水,如果类图不清晰,编写程序的过程就会变得寸步难行

题量及难度:

三个题集的题量不多,主要是围绕最后一题电梯程序展开的,三次电梯题目难度较大,我一次都没有编写正确,并且迭代也会增加一定难度。

二、设计与分析

在编写题集五中的单部电梯程序过程中,题目并没有要求编写多个类,实行单一职责原则,所以将所有的方法都放入电梯类当中,这样会方便方法的调用。但是对后期对程序进行维护十分不友好。
在编写过程中我也遇到了一些问题,就是不知道该如何规划以及设计,只是一股脑将所有的东西写入电梯类当中,在后续运行过程中发现结果并不符合实际情况,这时候就找不到是哪里出问题了,只能通过局部跟踪设置断点来排查程序问题,具体问题就是电梯在达到最高层后悔停留在最高层,不会继续处理请求,初步分析的原因是在达到最高层并没有立刻反向。

根据使用SourceMontor对我的题集五单部电梯调度程序进行分析,结果如下
image

文件信息

文件名:Main.java

总行数 (Lines):222

有效语句数 (Statements):124
文件实际执行的逻辑代码量较多。

分支语句百分比 (Percent Branch Statements):25.8%
分支语句比例适中,说明文件逻辑并非过于复杂。

函数调用语句数 (Method Call Statements):54
函数调用的数量占有效语句的其中一部分,暗示该程序具有较多的功能调用。

注释比例 (Percent Lines with Comments):4.5%
注释行占比非常低,仅 4.5%。需适当增加注释,提高代码的可读性和可维护性。

类与方法信息

类数 (Classes and Interfaces):4

方法数/类 (Methods per Class):3方法数/类 (Methods per Class):3.5
每个类包含 3.5 个方法,属于常见的类设计结构。

方法平均语句数 (Avg. Statements per Method):6.79
每个方法平均包含 6.79 条语句,方法粒度较细,有助于代码的可读性和可测试性。

复杂度与深度指标

最复杂方法 (Most Complex Method):Elevator.processCurrentFloor()

最大复杂度 (Maximum Complexity):10
表示控制流复杂度较高(常见值为 1-10),说明代码逻辑复杂。

最大嵌套深度 (Maximum Block Depth):5
表示嵌套层次较少(如 if、while 等嵌套)。这是良好设计的体现。

平均块深度 (Average Block Depth):2.31
平均嵌套深度为 2.31,进一步确认了代码的逻辑相对直观。

Kiviat GraphKiviat 图

Avg Complexity:平均复杂度较高,逻辑较为复杂。
% Comments:注释比例低于理想值,需要优化注释。
Max Depth 和 Max Complexity:值低表明代码有过深的嵌套,也有复杂的分支逻辑。

Block Histogram块直方图

块结构语句分布显示:
大多数块的语句数为 2-3,且没有超过 6 语句的块。表明方法实现短小精悍,符合最佳实践。

题集六是在题集五的基础上进行了迭代升级,具体要求是将电梯类里面的职责分开来,实现单一职责原则,让程序更加便于维护,在编写这个程序的过程中,我也遇到了一些困难,比如一开始搞不清职责该放到哪个类里面,后面根据学习黑马程序员中的课程,是谁干活就应该把方法放到谁的类里面,再根据关系创建对象调用方法。

根据使用SourceMontor对我的题集六单部电梯调度程序进行分析,结果如下
image
类图:image

  1. 基础指标分析
    文件规模:222行代码,124条有效语句,属于中等规模控制类文件
    注释覆盖率:4.5%(严重不足),建议提升至20-30%的行业标准
    分支复杂度:25.8%分支语句占比,显示存在较复杂的业务逻辑判断
  2. 类设计评估
    类结构:4个核心类(Elevator/Controller/Queue/Request)符合SRP原则
    方法分布:平均每个类3.5个方法,方法粒度控制合理
    方法复杂度:平均6.79语句/方法,但存在最大复杂度10的方法(processCurrentFloor())
  3. 深度与复杂度
    嵌套控制:最大深度5层(建议重构超过4层的嵌套逻辑)
    平均深度:2.31显示整体嵌套控制良好
    复杂度热点:Kiviat图显示Avg Complexity偏高,需重点优化调度算法

三、踩坑心得

image

单部电梯程序编写完成后,在运行程序的过程中,发现结果与预计不符合,根据分析,发现电梯程序在达到最高层之后就停在了最高层,并没有继续处理反方向的请求,初步预测可能是程序在处理完该方向上的请求并没有立刻改变方向,从而无法继续处理请求,根据代码分析,情况如下
1、ElevatorController类的findNextRequest()方法没有处理到达边界楼层的情况
2、processArrival()方法执行后没有自动触发方向反转逻辑
3、边界条件检测缺失,导致电梯到达终点后停止运行

经过修改,成功解决了该问题,修正后的代码如下图,增加了边界检测的方法以及调用了该方法
image
image

四、改进建议

一、架构设计改进

1、职责分离不足
问题:Elevator类承担了太多职责(请求管理、移动逻辑、状态维护)
改进:// 拆分为三个类:
class ElevatorController { /* 调度逻辑 / }
class RequestManager { /
管理内外请求 / }
class Elevator { /
仅维护电梯状态和移动 */ }
2、缺乏接口抽象
问题:直接使用具体类实现,难以扩展
改进:
interface IElevator {
void moveTo(int floor);
void addRequest(Request request);
}

二、代码优化

1、请求管理优化
问题:使用三个独立列表存储请求,容易不同步
改进:class Request {
int floor;
Direction direction; // NULL表示内部请求
long timestamp; // 用于优先级排序
}
2、目标楼层计算逻辑
问题:getTargetFloor()方法过于复杂(圈复杂度高)
改进:private int calculateTarget() {
return Stream.concat(
internalRequests.stream(),
externalRequests.stream()
).filter(this::isValidRequest)
.min(Comparator.comparingInt(r ->
Math.abs(r.floor - currentFloor)))
.map(r -> r.floor)
.orElse(currentFloor);
}
3、方向控制改进
问题:边界检查不完善,可能卡在端点
改进:private void updateDirection() {
if (currentFloor >= maxFloor) {
direction = Direction.DOWN;
} else if (currentFloor <= minFloor) {
direction = Direction.UP;
}
}

五、总结

1. 学到了什么?

1、基础编程能力:学会了用Java写类、方法、循环和条件判断,比如电梯的移动、开关门逻辑。
2、代码结构优化:知道把大段代码拆成小方法(比如move()、handleArrival()),让程序更清晰。
3、调试技巧:通过System.out.println()找Bug,比如发现电梯到顶层不转向的问题。
4、输入验证:用正则表达式(如<3,UP>)检查输入格式对不对。

2. 哪些地方需要加强?

1、代码复用:有些重复代码可以抽成方法,比如方向判断的逻辑。
注释和命名:方法名和变量名可以更清楚(比如dir改成direction),关键逻辑加注释。
2、边界条件测试:多测试极端情况,比如电梯在最高层或最底层时的行为。

3. 改进建议(对课程/作业)

1、分步骤教学:
先教单电梯基础功能(上下移动、开关门),再教请求调度,最后加多电梯协作。避免一开始就写复杂逻辑,容易混乱。
2、提供参考代码结构:
给一个基础框架(比如Elevator类里该有哪些方法),让学生填空完善,而不是从头写。

总而言之,在经过了改题目三次迭代之后,让我更加理解了什么是面向对象编程,领悟了一些面向对象编程的思想,在编写程序的过程中,要严格遵守单一职责原则,搞清类与对象之间的关系,在编写代码之前设计好类与类中的方法,不能脚踩西瓜皮滑到哪里是哪里,这样写出来的代码才会更加具有可延续性

posted @ 2025-04-19 22:54  kikol  阅读(54)  评论(0)    收藏  举报