Java 实现电梯逻辑

一、实现结果说明

这里首先说明实现结果:

1、已实现:

  • 实现电梯的移动逻辑。
  • 实现了电梯外部的每个楼层的上下按钮。
  • 实现了电梯运行的同时添加新楼层。

2、未实现:

  • 没有实现电梯内部的按钮。
  • 没有实现多个电梯协同运行。
  • 没有实现电梯开关门时的逻辑。

二、电梯运行的情况

  • 当电梯向上移动时,会一直运行至发出请求的所有楼层中最高的楼层。
  • 向下移动时,会一直运行至发生请求的所有楼层中最低的楼层。
  • 在电梯运行过程中,如果有用户点击了某一层的按钮,会根据该层的按钮与当前电梯所在的层数和电梯要去的层数相比较,以及判断电梯的运行方向,来确定下一步去往的楼层。

三、实现说明

该代码实现使用 Java 编写,使用多线程来分析处理电梯的移动,以及各个楼层的按钮点击处理。

当然,没有展示的页面,Java 编写可视化页面还是相当吃翔的。采用控制台输出的方式来告诉开发者现在电梯所在的楼层。

实现代码中目前一共包含七个类(多数属于非严格的单例对象):

  • Lift.java:负责电梯的移动,从任务列表中取得任务,并判断电梯应该运行的方向。
  • LayerRequest.java :这个类是定义的一个数据结构,用来保存每个楼层的请求。负责处理电梯获取或者删除任务的请求,以及各个楼层召唤电梯的请求。
  • LayerList.java:该类保存着每个楼层。是一个继承了 ArrayList 的类。
  • Layer.java:该类表示的是单个楼层,存储着某个楼层的信息。
  • MoveDirection.java:电梯的移动方向,电梯的移动方向有三种:UP、DOWN、STOP。
  • Client.java:客户端处理类,电梯与外界交互就靠这一个类,可以使用该类向电梯发送上升或者下降的请求。同时该类管理着一个线程池。
  • Test.java:测试类。

四、部分代码解析

如果要查看源代码,可以从 CSDN 上下载 ZIP 文件 CSDN ------ Java 实现电梯逻辑

同时也提供了 GitHub 项目地址:GitHub ------ Java 实现电梯逻辑

1、Lift.java 核心代码

java 复制代码
/**
     * 向上移动电梯
     */
    private void moveUp() {
        int currentLayerNumber = this.getCurrentLayer().getLayerNumber();
        int targetLayerNumber;
        while (currentLayerNumber < (targetLayerNumber = this.getTargetLayer().getLayerNumber())) {
            this.moving();
            Layer layer = this.layerList.get(currentLayerNumber);
            this.setCurrentLayer(layer);
            currentLayerNumber++;
            if (currentLayerNumber != targetLayerNumber) {
                this.passLayer(layer);
            }
        }
        this.reachTargetLayer();
    }

    /**
     * 向下移动电梯
     */
    private void moveDown() {
        int currentLayerNumber = this.getCurrentLayer().getLayerNumber();
        int targetLayerNumber;
        while (currentLayerNumber > (targetLayerNumber = this.getTargetLayer().getLayerNumber())) {
            this.moving();
            // 这里减二是因为:
            // 需要通过索引获取楼层, getLayerNumber() 对索引进行了加一, 需要减一获得索引,
            // 而这里是电梯下降, 需要获取下一个楼层的索引, 所以还要再减一
            Layer layer = this.layerList.get(currentLayerNumber - 2);
            this.setCurrentLayer(layer);
            currentLayerNumber--;
            if (currentLayerNumber != targetLayerNumber) {
                this.passLayer(layer);
            }
        }
        this.reachTargetLayer();
    }

    /**
     * 移动电梯到目标楼层
     */
    private void move(int diff) {
        if (diff > 0) {
            moveDown();
        } else {
            moveUp();
        }
    }

    /**
     * 电梯运行, 主要负责电梯的移动
     */
    void run() {
        while (this.runnable()) {
            try {
                this.setUsing(this.layerRequest.hasTask());
                if (!this.isUsing()) {
                    continue;
                }
                // 电梯有任务才会执行核心函数
                this.runCore();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 电梯是否可运行
     *
     * @return 可运行返回 true
     */
    private boolean runnable() {
        return !isFault();
    }

    /**
     * 电梯运行核心 (我是这样起名的, 它配不配这个名字我就不知道了)<br/>
     * 此时电梯一定处于 stop 状态
     */
    private void runCore() {
        Layer layer;
        LayerRequest layerRequest = this.layerRequest;
        int diff;
        int currentLayerNumber = this.getCurrentLayer().getLayerNumber();
        int targetLayerNumber = this.getTargetLayer().getLayerNumber();

        // 根据 当前楼层 与 目标楼层 的相对位置来设置电梯移动方向
        if ((diff = currentLayerNumber - targetLayerNumber) < 0) {
            layer = layerRequest.getLayer();
            if (layer != null) {
                this.setCurrentMoveDirection(MoveDirection.UP);
            } else {
                this.setCurrentMoveDirection(MoveDirection.DOWN);
            }
        } else if ((diff = currentLayerNumber - targetLayerNumber) > 0) {
            layer = layerRequest.getLayer();
            if (layer != null) {
                this.setCurrentMoveDirection(MoveDirection.DOWN);
            } else {
                this.setCurrentMoveDirection(MoveDirection.UP);
            }
        } else {
            return;
        }

        if (this.checkLayer(layer)) {
            this.setTargetLayer(layer);
            this.move(diff);
        }
    }

    /**
     * 检查楼层所属的区间, 下面是 layer 楼层所在的不同区间的所有的返回结果: <br/>
     * 一. [ (layer: -1) 低楼层 -- (layer: 0) --> 高楼层 (layer: 1) ] <br/>
     * 二. [ (layer: -1) 高楼层 -- (layer: 0) --> 低楼层 (layer: 1) ] <br/>
     * 三. 电梯处于 stop 状态时若电梯处于 stop 状态, 返回 layer 与 currentLayer 的楼层差值
     *
     * @param layer 要检查的楼层
     * @return 返回数字, 表示 layer 楼层所属的区间
     */
    int checkLayerInRange(Layer layer) {
        Layer currentLayer = this.getCurrentLayer();
        Layer targetLayer = this.getTargetLayer();
        int currentLayerNumber = currentLayer.getLayerNumber();
        int targetLayerNumber = targetLayer.getLayerNumber();

        int layerNumber = layer.getLayerNumber();

        // 上升时, 返回值取决于楼层 layer 所在的区间: [ (layer: -1) 低楼层 -- (layer: 0) --> 高楼层 (layer: 1) ]
        if (isMoveUp()) {
            if (layerNumber < currentLayerNumber) {
                return -1;
            } else if (targetLayerNumber < layerNumber) {
                return 1;
            } else {
                return 0;
            }
        }
        // 下降时, 返回值取决于 layer 所在的区间: [ (layer: -1) 高楼层 -- (layer: 0) --> 低楼层 (layer: 1) ]
        else if (isMoveDown()) {
            if (layerNumber < targetLayerNumber) {
                return 1;
            } else if (layerNumber > currentLayerNumber) {
                return -1;
            } else {
                return 0;
            }
        }
        // 若电梯处于 stop 状态, 返回 layerNumber 与 currentLayerNumber 的差值
        else {
            return layerNumber - currentLayerNumber;
        }
    }

2、LiftRequest.java 核心代码

java 复制代码
void removeUpLayer() {
        this.removeLayer(this.nextUpList, this.nextDownList, MoveDirection.UP);
    }

    void removeDownLayer() {
        this.removeLayer(this.nextDownList, this.nextUpList, MoveDirection.DOWN);
    }

    /**
     * 电梯到达目标楼层时移除楼层, 从 usingList 中移除 <br/>
     * 当 usingList 中没有楼层时, 则设置 freeList 的第一个元素为 {@link Lift#targetLayer}, freeList 将成为 usingList<br/>
     *
     * @param nextUsingList 下一执行阶段要执行的任务
     * @param nextFreeList  下一执行阶段要执行的任务
     * @param moveDirection 当前电梯的运行状态
     */
    private void removeLayer(List<Layer> nextUsingList, List<Layer> nextFreeList,
                             MoveDirection moveDirection) {
        Lift lift = this.lift;
        List<Layer> taskList = this.taskList;

        // 当前任务执行完成, 将其移除
        removeFirst();

        // 移除后如果任务列表不为空, 就将列表第一个楼层设为目标楼层
        if (!taskList.isEmpty()) {
            lift.setTargetLayer(getFirst());
            return;
        }

        // 这段代码在下面的情况下生效 (电梯发生转向时):
        // 例如: 电梯从第一层移动到第七层, 在电梯到达第五层时, 此时在第三层按下向下的按钮, 将会添加到 nextFreeList 集合中
        if (!nextFreeList.isEmpty()) {
            taskList.addAll(nextFreeList);
            // 根据不同的移动状态排序
            if (MoveDirection.isMoveUp(moveDirection)) {
                this.reserveSort();
            } else if (MoveDirection.isMoveDown(moveDirection)) {
                this.sort();
            }
            lift.setTargetLayer(getFirst());
            nextFreeList.clear();
        }

        // 如果电梯反向运行列表没有元素 (nextFreeList 为空, empty), 就执行同向的任务列表
        // 例如: 电梯要从第一层移动到第七层, 并且电梯已经移动到第四层, 此时点击第一层的上升按钮和第三层的上升按钮,
        // 将会添加到 nextUsingList 集合中
        // 电梯移动过程: (1): 1 --- 上升 ---> 7  (2): 7 --- 下降 ---> 1  (3): 1 --- 上升 ---> 3
        if (taskList.isEmpty() && !nextUsingList.isEmpty()) {
            taskList.addAll(nextUsingList);
            if (MoveDirection.isMoveUp(moveDirection)) {
                this.sort();
            } else if (MoveDirection.isMoveDown(moveDirection)) {
                this.reserveSort();
            }
            lift.setTargetLayer(getFirst());
            nextUsingList.clear();
        }
    }

    /**
     * 添加楼层
     * @param layer 要添加的楼层
     * @param moveDirection 要去往的方向
     */
    void addLayer(Layer layer, MoveDirection moveDirection) {
        if (!this.taskList.contains(layer)) {
            Lift lift = this.lift;
            if (lift.getCurrentLayer().equals(layer)) {
                this.alreadyLocated(layer);
                return;
            }
            lift.setTargetLayerIfNull(layer);
            int result = lift.checkLayerInRange(layer);
            // 如果电梯处于停止状态
            if (lift.isMoveStop()) {
                if (result > 0) {
                    this.addUpLayerWithSort(layer);
                    lift.setCurrentMoveDirection(MoveDirection.UP);
                } else if (result < 0) {
                    this.addDownLayerWithSort(layer);
                    lift.setCurrentMoveDirection(MoveDirection.DOWN);
                }
                lift.setTargetLayer(layer);
                return;
            }
            // 根据按钮点击的是上升还是下降来调用
            if (MoveDirection.isMoveUp(moveDirection)) {
                this.addUpLayer(result, layer);
            } else {
                this.addDownLayer(result, layer);
            }
        }
    }

    /**
     * 添加要上楼的楼层
     *
     * @param result result
     * @param layer 要添加的楼层
     */
    private void addUpLayer(int result, Layer layer) {
        Lift lift = this.lift;
        if (lift.isMoveUp()) {
            if (result == 0) {
                lift.setTargetLayer(layer);
                this.addUpLayerWithSort(layer);
            } else if (result == 1) {
                this.addUpLayerWithSort(layer);
            } else if (result == -1) {
                this.addLayerIfNotExist(this.nextUpList, layer);
            }
        } else if (lift.isMoveDown()) {
            this.addLayerIfNotExist(this.nextUpList, layer);
        }
    }

    /**
     * 添加要下楼的楼层
     *
     * @param layer 要添加的楼层
     */
    void addDownLayer(int result, Layer layer) {
        Lift lift = this.lift;
        if (lift.isMoveDown()) {
            if (result == 0) {
                lift.setTargetLayer(layer);
                this.addDownLayerWithSort(layer);
            } else if (result == 1) {
                this.addDownLayerWithSort(layer);
            } else if (result == -1) {
                this.addLayerIfNotExist(this.nextDownList, layer);
            }
        } else if (lift.isMoveUp()) {
            this.addLayerIfNotExist(this.nextDownList, layer);
        }
    }

五、有话说

有兴趣的小伙伴可以自己写一个类似的程序,或者在此基础上做修改、加上新的处理逻辑,代码如有瑕疵,敬请见谅!

相关推荐
新手小袁_J13 分钟前
JDK11下载安装和配置超详细过程
java·spring cloud·jdk·maven·mybatis·jdk11
呆呆小雅14 分钟前
C#关键字volatile
java·redis·c#
Monly2114 分钟前
Java(若依):修改Tomcat的版本
java·开发语言·tomcat
Ttang2316 分钟前
Tomcat原理(6)——tomcat完整实现
java·tomcat
钱多多_qdd27 分钟前
spring cache源码解析(四)——从@EnableCaching开始来阅读源码
java·spring boot·spring
waicsdn_haha29 分钟前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
Q_192849990639 分钟前
基于Spring Boot的摄影器材租赁回收系统
java·spring boot·后端
Code_流苏42 分钟前
VSCode搭建Java开发环境 2024保姆级安装教程(Java环境搭建+VSCode安装+运行测试+背景图设置)
java·ide·vscode·搭建·java开发环境
禁默1 小时前
深入浅出:AWT的基本组件及其应用
java·开发语言·界面编程
Cachel wood2 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架