我的第一次博客--电梯调度问题

电梯调度系统演进报告

目录

前言

设计与分析

第一次电梯调度设计

第二次电梯调度设计

第三次电梯调度设计

采坑心得

改进建议

总结

前言

吐槽一下自己:

  • 刚开始接触老师口中的难题时,对此不以为然,认为以我的实力还不是一两天就轻松解决它。哈哈,很快我就被打脸了,但我还不以为意我认为一定是题目出错了,不然怎么可能一个星期快过去了还没有人做出来。后面有人正确了,我抱着不可思议的神态去请教了他,此时我发现我的思路从头到尾都是错的。我从这明白了“你认为的都是错的”这句话的含义(我没有根据实验需求去做它,而是按照我认为电梯应该运行的方式去编写了代码 😢 )。

从无到有的艰辛

第一次写电梯问题这时是最为困难的
遇到的困难:

  1. 如何正确地处理输入数据并进行储存和处理:
  • 我列出了map,queue,数组 三种方式去储存数据。(最为熟悉数组,最终选择了数组)
  • 学习了关于matches(),Integer.parseInt(),startsWith(),endsWith(),substring(),split()等函数去处理输入数据并储存在数组中。
  1. 分析电梯调度的方式:
  • 刚开始时我是通过将所有的请求全部用一个个数组储存,再进行整合排序,最后再按顺序进行处理。(虽然我的思路错了但我画了大量时间使其尽量完美:1.通过分组、排序、合并优化了调度逻辑 2. 通过预处理减少方向切换次数 3. 实现SCAN算法优化.)这让我明白了:当你的代码没有按照需求去进行时,你再多的努力都是白费的。
  • 后明白需求后在前面的基础上通过请求队列处理和方向决策算法将题目完成,这一阶段仅仅只用了两天不到就完成了。

初次迭代

  1. 将初次代码中的储存方式进行了修改:
  • 将数组储存改为了链表储存。
  • 学习了util.LinkedList中有关函数的调用以确保修改的顺利。
  1. 对运行方法代码进行了优化:
  • 增加了对无效输入的处理(超出有效楼层)。
  • 完善了电梯运行时遇到特殊情况电梯的转向与否。
  1. 改善了代码可读性与可维护性和可扩展性
  • 将开始代码中的一个电梯类改为了(电梯类,外部请求类,请求队列类,控制器类)四个主要类别。

再次迭代

  1. 对输入数据的处理微调(输入格式的改变)
  • 在外部储存类中加了一个最终楼层。
  • 开始时的直接获取UP,DOWN改为了对初始楼层与最终楼层的判断得出。
  1. 微调了处理数据方式
  • 通过统一请求模型和自动请求转换提升了系统的真实性。
  1. 继续完善代码可读性与可维护性和可扩展性
  • 新增了Passenger类,用其抽象化所有请求,减少了代码冗余。
  • 逻辑优化了控制器类,控制器逻辑更贴近现实电梯的调度行为。

设计与分析

第一次电梯调度设计

第一次电梯源码:

Gitee Repo

类与方法设计:
点击查看
class Dianti {
  // 电梯属性
  private int max;
  private int min;
  private int currentceng;
  private int currentturn;
  private int currenttai;
  // 请求存储结构
  private int[][] qingqouceng;
  private int[] qingqouUpDown;
  private int[] qingqoumember;
  //构造函数
  Dianti(int max, int min, int currenttai) {}
  // Getter/Setter方法
  public int getMax() {}
  public int getMin() {}
  public int getCurrentceng() {}
  public int getCurrentturn() {}
  public void setCurrentturn(int currentturn) {}
  public int getCurrenttai() {}
  // 电梯动作方法
  public void turnup() {}
  public void turndown() {}
  public void turnturnup() {}
  public void turnturndown() {}
  public void turnontai() {}
  public void turnofftai() {}
  //处理用户输入
  public void processInput() {}
  //运行电梯主逻辑
  public void runElevator() {}
}

public class Main{}

电梯调度方式解析:
  1. 请求存储结构
  • (1)qingqouceng[2][1000]:二维数组,第一维[0]存储外部请求楼层,[1]存储内部请求楼层
  • (2)qingqouUpDown[1000]:数组,存储外部请求的方向(1=上,2=下)
  • (3)qingqoumember[2]:计数器,[0]记录外部请求数量,[1]记录内部请求数量
  1. 请求处理主逻辑
  • (1)电梯初始方向设置为向上
  • (2)使用两个指针i(外部请求)和o(内部请求)来跟踪已处理的请求
  • (3)每次循环先打印当前楼层和方向
  • (4)检查外部请求:
    如果当前楼层有外部请求(aa == qingqouceng[0][i])。且方向匹配(bb == qingqouUpDown[i]或没有内部请求需要处(o==qingqoumember[1]) ◦ 则开门、关门,并移动外部请求指针i++
  • (5)检查内部请求:
    如果当前楼层有内部请求(aa == qingqouceng[1][o])。且不是与外部请求同楼层(避免重复开门)则开门、关门,并移动内部请求指针o++
  • (6)方向决策:
    如果所有外部请求已处理完(i == qingqoumember[0]),则根据下一个内部请求位置决定方向
    如果所有内部请求已处理完(o == qingqoumember[1]),则根据下一个外部请求位置决定方向
    如果当前楼层高于所有剩余请求(aa > qingqouceng[0][i] && aa > qingqouceng[1][o]),则向下
    如果当前楼层低于所有剩余请求(aa < qingqouceng[0][i] && aa < qingqouceng[1][o]),则向上
  • (7)移动机制:
    方向向上(currentturn == 1):调用turnup()使当前楼层+1
    方向向下(currentturn == 2):调用turndown()使当前楼层-1
Java源代码代码质量度量指标图
点击查看

对上面的数据进行分析
  1. % Branches行业通常要求≥80%,而我的代码仅到29.8%。这表示我的代码逻辑不完善可能存在隐藏缺陷
  2. 文件中调用其他方法的次数为31,调用次数较少,说明我的代码可能有大量重复代码
  3. 仅仅只有2个类,单个类的功能太过繁杂,无法保证单一职责原则,应该将不同的功能分开
  4. 最大复杂度为10,我的代码多重嵌套了if-else与方法过长(应该拆分为多个小方法)
  5. 代码块的最大嵌套深度为5 ,这表示我的代码可读性差与维护困难

第二次电梯调度设计

第二次电梯源码:

Gitee Repo

类与方法设计:
点击查看
enum Direction {
    UP,   // 上行
    DOWN, // 下行
    IDLE  // 空闲
}
enum State {
    MOVING,  // 移动中
    STOPPED  // 已停止
}

//电梯类
class Elevator {   
    // 属性
    private int currentFloor;  // 当前楼层
    private Direction direction; // 运行方向
    private State state;       // 运行状态
    private int maxFloor;      // 最高楼层
    private int minFloor;      // 最低楼层
    // 构造方法
    public Elevator(int minFloor, int maxFloor) {}

    // 电梯动作方法
    public void turnup() {}      // 上升一层
    public void turndown() {}    // 下降一层
    public void turnturnup() {}  // 设置上行方向
    public void turnturndown() {}// 设置下行方向

    // Getter/Setter
    public int getCurrentFloor() {}
    public void setCurrentFloor(int currentFloor) {}
    public Direction getDirection() {}
    public void setDirection(Direction direction) {}
    public State getState() {}
    public void setState(State state) {}
    public int getMaxFloor() {}
    public int getMinFloor() {}
    // 校验方法
    public boolean isValidFloor(int floor) {} // 验证楼层有效性
}

//外部请求类
class ExternalRequest {
    private Integer floor;     // 请求楼层
    private Direction direction; // 请求方向
    // 构造方法
    public ExternalRequest(Integer floor, Direction direction) {}
    // Getter方法
    public Integer getFloor() {}
    public Direction getDirection() {}
}

//请求队列管理类
class RequestQueue {
    private LinkedList<Integer> internalRequests;      // 内部请求队列
    private LinkedList<ExternalRequest> externalRequests; // 外部请求队列

    public RequestQueue() {}

    // 队列管理方法
    public RequestQueue getQueueInstance() {} // 获取队列实例
    public LinkedList<Integer> getInternalRequests() {}
    public void setInternalRequests(LinkedList<Integer> internalRequests) {}
    public LinkedList<ExternalRequest> getExternalRequests() {}
    public void setExternalRequests(LinkedList<ExternalRequest> externalRequests) {}
    public void addInternalRequest(int floor) {}      // 添加内部请求
    public void addExternalRequest(int floor, Direction direction) {} // 添加外部请求
}

//电梯控制器类
class Controller {
    private Elevator elevator;   // 电梯实例
    private RequestQueue queue;  // 请求队列

    // 构造方法
    public Controller() {}
    public Controller(Elevator elevator, RequestQueue requestQueue) {}

    // 控制方法
    public void yesornoturn(int currentF, Direction currentD, Integer exterF,Direction exterD, Integer inter) {} // 转向判断                       
    public int yesornowai(int currentF, Direction currentD, Integer exterF,Direction exterD, Integer inter) {}  // 外部请求处理判断                
    public int yesornonei(int currentF, Direction currentD, Integer exterF,Direction exterD, Integer inter) {}  // 内部请求处理判断
    public void runElevator() {} // 电梯运行主逻辑
    public void processInput() {} // 处理用户输入

    // Getter/Setter
    public Elevator getElevator() {}
    public void setElevator(Elevator elevator) {}
    public RequestQueue getQueue() {}
    public void setQueue(RequestQueue queue) {}
}

//主程序入口
public class Main {}

电梯调度方式解析:
  1. 请求存储结构改变
  • (1) LinkedList:存储外部请求队列(包含楼层和方向)
  • (2) LinkedList:存储内部请求队列(仅包含目标楼层)
  • (3) 采用队列数据结构实现先进先出的请求处理顺序
  1. 请求处理主逻辑
    对比于第一次增加的功能
  • (1)增加了范围检查
  • (2)新增请求去重功能
  • (3)增加中间层方向修正
    电梯正在上行(currentD == UP),且当前楼层(currentF)高于下一个内部请求的目标楼层(inter)。立即转向下行(turnturndown())
    电梯正在下行(currentD == DOWN),且当前楼层(currentF)低于下一个内部请求的目标楼层(inter)。立即转向上行(turnturnup())
Java源代码代码质量度量指标图
点击查看

从上数据分析

相较于第一次进步的地方

  1. 类/接口数由2加到7使得架构分层更清晰
  2. 分支语句占比由29.8%到21.8%逻辑更线性
    不足的地方相近

第三次电梯调度设计

第三次电梯源码:

Gitee Repo

类与方法设计:
点击查看
//电梯运行方向枚举
enum Direction {
    UP, DOWN, IDLE
}

//电梯运行状态枚举
enum State {
    MOVING, STOPPED
}

//电梯类
class Elevator {
    // 属性
    private int currentFloor;
    private Direction direction;
    private State state;
    private int maxFloor;
    private int minFloor;

    // 构造方法
    public Elevator(int minFloor, int maxFloor) {}

    // 电梯动作方法
    public void turnup() {}
    public void turndown() {}
    public void turnturnup() {}
    public void turnturndown() {}

    // Getter/Setter
    public int getCurrentFloor() {}
    public void setCurrentFloor(int currentFloor) {}
    public Direction getDirection() {}
    public void setDirection(Direction direction) {}
    public State getState() {}
    public void setState(State state) {}
    public int getMaxFloor() {}
    public int getMinFloor() {}

    // 校验方法
    public boolean isValidFloor(int floor) {}
}

//乘客请求类
class Passenger {
    // 属性
    private Integer sourceFloor;
    private Integer destinationFloor;
    private Direction direction;

    // 构造方法
    public Passenger(Integer sourceFloor, Integer destinationFloor, Direction direction) {}
    public Passenger(Integer destinationFloor) {}

    // Getter/Setter
    public Integer getSourceFloor() {}
    public void setSourceFloor(Integer sourceFloor) {}
    public Integer getDestinationeFloor() {}
    public void setDestinationFloor(Integer destinationFloor) {}
    public Direction getDirection() {}
}

//请求队列类
class RequestQueue {
    // 属性
    private LinkedList<Passenger> internalRequests;
    private LinkedList<Passenger> externalRequests;

    // 构造方法
    public RequestQueue() {}

    // 队列管理
    public RequestQueue getQueueInstance() {}
    public LinkedList<Passenger> getInternalRequests() {}
    public void setInternalRequests(LinkedList<Passenger> internalRequests) {}
    public LinkedList<Passenger> getExternalRequests() {}
    public void setExternalRequests(LinkedList<Passenger> externalRequests) {}
    public void addInternalRequest(int floor) {}
    public void addExternalRequest(int floor1, int floor2, Direction direction) {}
}

//控制器类
class Controller {
    // 属性
    private Elevator elevator;
    private RequestQueue queue;

    // 构造方法
    public Controller() {}
    public Controller(Elevator elevator, RequestQueue requestQueue) {}

    // 核心方法
    public void processInput() {}
    public void runElevator() {}
    private void determineDirection(int currentF, Direction currentD, Integer exterF, Direction exterD, Integer inter) {}                                  
    private int processExternalRequest(int currentF, Direction currentD, Integer exterF, Direction exterD, Integer inter) {}
    private int processInternalRequest(int currentF, Direction currentD, Integer exterF, Direction exterD, Integer inter) {}
                                     
    // Getter/Setter
    public Elevator getElevator() {}
    public void setElevator(Elevator elevator) {}
    public RequestQueue getQueue() {}
    public void setQueue(RequestQueue queue) {}
}

//主程序入口
public class Main {}

电梯调度方式解析:
  1. 请求存储结构基本不变
    只是改变储存外部请求数据 由<数字,方向>为<数字,数字 >
    方向储存通过判断同样进行储存
  2. 请求处理主逻辑基本不变
    加入了使得<数字,数字 >处理到前面的数字时将后面的储存到内部请求末尾
Java源代码代码质量度量指标图
点击查看

从上数据分析进步并不多

采坑心得

心得

万事开头难,坚持必过关;破局见坦途,后续自悠然。

我第一次的电梯写了许久,也错了许久,但后来的两次我都是迅速通关的。

点击查看提交图片


第一次提交忘记删除测试终止条件了

  • 一定要先将题目/客户需求分析透彻再进行对代码设计

  • 主要的错误点与查找方式

  1. 非零返回(1.检查使用方法前是否将该类进行初始化 2. 是否使用了为空的数组/队列)
  2. 运行超时(如果时代码效率问题就去提高代码效率)(如果是无法出循环,可以定一个参数来控制循环次数从而找到无法出循环的原因)
  3. 编译错误(建议在多个地方加入输出点如printf从而找出从哪开始出错,再进行解决)

改进建议

架构设计改进

  • 我的电梯代码中的direction和state通过多个if-else管理,这使得维护起来十分困难
  • Controller同时处理输入和调度,违反单一职责

代码实现优化

  • runElevator()方法复杂度为14,这个方法过长了需要进行拆分
  • 有一些特殊情况未考虑到

代码改进目标

指标 当前值 改进后要到达的目标
最大方法复杂度 15 ≤5
平均块深度 2.11 ≤1.8
注释覆盖率 24.8% ≥35%
分支语句占比 19.1% ↓15%

总结

三次代码迭代

初始版本(数组+简单SCAN)
  • 特点:
    固定大小数组存储请求
  • 问题:
    缺乏扩展性,条件判断冗余
优化版本(队列+OOP)
  • 改进:
    采用LinkedList动态管理请求
    引入Direction/State枚举明确状态
    分离控制器与电梯实体
  • 问题:
    runElevator()方法仍过于复杂(复杂度15)
最终版(设计模式+防御性编程)
  • 提升:
    统一Passenger请求模型
    智能转向策略(currentD != exterD时动态调整)
    输入自动校验

收获

  • 学会通过类职责划分降低耦合
  • 学会通过方法拆分降低圈复杂度
  • 学会新增参数校验方法以达到防御性编程目的
  • 认识到注释和文档的重要性

需要进一步学习

  • 保证单一职责原则
  • 控制方法长度
  • 减少参数传递
  • 避免深层嵌套
  • 提取工具方法
posted @ 2025-04-18 21:55  AstraX  阅读(77)  评论(0)    收藏  举报