luo-9

 

第一次Blog作业-单部电梯调度问题

题目

单部电梯调度程序

设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。

电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。

使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。

请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求),具体运行规则详见输入输出样例。

输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。

电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。

输出格式:
模拟电梯的运行过程,输出方式如下:

运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
输入样例:

点击查看代码
1
20
<3,UP>
<5>
<6,DOWN>
<7>
<3>
end

输出样例:

点击查看代码
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door()
**电梯运行过程详解**

https://images.ptausercontent.com/b980fd0a-0919-48a1-bc7d-c4748c4e1ea5.pdf

以下是我第一次源码,其中存在一些问题(为了方便导入取消了多文件格式)。

点击查看代码
import java.util.*;

public class Elevator {
    private int minFloor;
    private int maxFloor;
    private int currentFloor;
    private Direction direction;
    private ElevatorState state;
    private Queue<Integer> innerRequests; // 电梯内请求队列
    private Queue<FloorRequest> outerRequestsUp; // 上行请求队列
    private Queue<FloorRequest> outerRequestsDown; // 下行请求队列

    // 枚举类型:方向
    public enum Direction {
        UP, DOWN, IDLE
    }

    // 枚举类型:电梯状态
    public enum ElevatorState {
        MOVING, STOPPED
    }

    // 请求的楼层和方向
    public static class FloorRequest {
        int floor;
        Direction direction;

        FloorRequest(int floor, Direction direction) {
            this.floor = floor;
            this.direction = direction;
        }
    }

    // 构造方法
    public Elevator(int minFloor, int maxFloor) {
        if (minFloor > maxFloor) {
            throw new IllegalArgumentException("最小楼层不能大于最大楼层");
        }
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.currentFloor = 1;
        this.direction = Direction.IDLE;
        this.state = ElevatorState.STOPPED;
        this.innerRequests = new LinkedList<>();
        this.outerRequestsUp = new LinkedList<>();
        this.outerRequestsDown = new LinkedList<>();
    }

    // 检查并启动电梯
    public void start() {
        while (!isAllRequestsEmpty()) {
            if (state == ElevatorState.STOPPED) {
                determineDirection();
                state = ElevatorState.MOVING;
            }
            processRequests();
        }
        state = ElevatorState.STOPPED;
        direction = Direction.IDLE;
    }

    // 检查所有请求队列是否为空
    private boolean isAllRequestsEmpty() {
        return innerRequests.isEmpty() && outerRequestsUp.isEmpty() && outerRequestsDown.isEmpty();
    }

    // 确定电梯运行方向
    private void determineDirection() {
        if (!outerRequestsUp.isEmpty() && (direction!= Direction.DOWN || outerRequestsDown.isEmpty())) {
            direction = Direction.UP;
        } else if (!outerRequestsDown.isEmpty() && (direction!= Direction.UP || outerRequestsUp.isEmpty())) {
            direction = Direction.DOWN;
        } else if (!innerRequests.isEmpty()) {
            int firstInnerRequest = innerRequests.peek();
            if (firstInnerRequest > currentFloor) {
                direction = Direction.UP;
            } else if (firstInnerRequest < currentFloor) {
                direction = Direction.DOWN;
            }
        } else {
            direction = Direction.IDLE;
        }
    }

    // 处理请求
    private void processRequests() {
        if (direction == Direction.UP) {
            processUpRequests();
        } else if (direction == Direction.DOWN) {
            processDownRequests();
        } else {
            // 若方向为IDLE,检查是否有新请求并重新确定方向
            determineDirection();
            if (direction!= Direction.IDLE) {
                state = ElevatorState.MOVING;
                processRequests();
            }
        }
    }

    // 处理上行请求
    private void processUpRequests() {
        boolean hasOuterRequest = processOuterRequests(outerRequestsUp, Direction.UP);
        boolean hasInnerRequest = processInnerRequests(Direction.UP);

        if (!hasOuterRequest &&!hasInnerRequest) {
            // 上行方向没有请求了,检查下行是否有请求
            if (!outerRequestsDown.isEmpty()) {
                direction = Direction.DOWN;
                processDownRequests();
            } else {
                state = ElevatorState.STOPPED;
                direction = Direction.IDLE;
            }
        }
    }

    // 处理下行请求
    private void processDownRequests() {
        boolean hasOuterRequest = processOuterRequests(outerRequestsDown, Direction.DOWN);
        boolean hasInnerRequest = processInnerRequests(Direction.DOWN);

        if (!hasOuterRequest &&!hasInnerRequest) {
            // 下行方向没有请求了,检查上行是否有请求
            if (!outerRequestsUp.isEmpty()) {
                direction = Direction.UP;
                processUpRequests();
            } else {
                state = ElevatorState.STOPPED;
                direction = Direction.IDLE;
            }
        }
    }

    // 处理外部请求
    private boolean processOuterRequests(Queue<FloorRequest> requestQueue, Direction currentDirection) {
        boolean processedRequest = false;
        while (!requestQueue.isEmpty()) {
            FloorRequest request = requestQueue.poll();
            if (request.direction == currentDirection) {
                moveToFloor(request.floor);
                processedRequest = true;
            }
        }
        return processedRequest;
    }

    // 处理内部请求
    private boolean processInnerRequests(Direction currentDirection) {
        boolean processedRequest = false;
        Iterator<Integer> iterator = innerRequests.iterator();
        while (iterator.hasNext()) {
            int requestedFloor = iterator.next();
            if ((currentDirection == Direction.UP && requestedFloor > currentFloor) ||
                    (currentDirection == Direction.DOWN && requestedFloor < currentFloor)) {
                moveToFloor(requestedFloor);
                iterator.remove();
                processedRequest = true;
            }
        }
        return processedRequest;
    }

    // 模拟电梯到达楼层
    private void moveToFloor(int floor) {
        if (currentFloor!= floor) {
            if (floor > currentFloor) {
                direction = Direction.UP;
                while (currentFloor < floor) {
                    currentFloor++;
                    System.out.println("Current Floor: " + currentFloor + " Direction: " + direction);
                }
            } else {
                direction = Direction.DOWN;
                while (currentFloor > floor) {
                    currentFloor--;
                    System.out.println("Current Floor: " + currentFloor + " Direction: " + direction);
                }
            }
        }
        openAndCloseDoor(floor);
    }

    // 打开和关闭电梯门
    private void openAndCloseDoor(int floor) {
        System.out.println("Open Door # Floor " + floor);
        System.out.println("Close Door");
    }

    // 处理外部请求
    public void addOuterRequest(int floor, Direction direction) {
        if (isValidFloor(floor)) {
            if (direction == Direction.UP && floor > currentFloor) {
                outerRequestsUp.add(new FloorRequest(floor, direction));
            } else if (direction == Direction.DOWN && floor < currentFloor) {
                outerRequestsDown.add(new FloorRequest(floor, direction));
            }
        } else {
            System.out.println("无效的楼层请求: " + floor);
        }
    }

    // 处理内部请求
    public void addInnerRequest(int floor) {
        if (isValidFloor(floor)) {
            innerRequests.add(floor);
        } else {
            System.out.println("无效的楼层请求: " + floor);
        }
    }

    // 检查楼层是否有效
    private boolean isValidFloor(int floor) {
        return floor >= minFloor && floor <= maxFloor;
    }

    // 测试电梯运行
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        try {
            // 输入最小和最大楼层
            int minFloor = scanner.nextInt();
            int maxFloor = scanner.nextInt();
            scanner.nextLine(); // 清理输入缓冲区

            // 创建电梯对象
            Elevator elevator = new Elevator(minFloor, maxFloor);

            // 输入请求
            String line;
            while (!(line = scanner.nextLine()).equalsIgnoreCase("end")) {
                if (line.startsWith("<") && line.endsWith(">")) {
                    String content = line.substring(1, line.length() - 1);
                    if (content.contains(",")) {
                        // 处理外部请求 <楼层,方向>
                        String[] parts = content.split(",");
                        int floor = Integer.parseInt(parts[0]);
                        Direction direction = Direction.valueOf(parts[1].toUpperCase());
                        elevator.addOuterRequest(floor, direction);
                    } else {
                        // 处理内部请求 <楼层>
                        int floor = Integer.parseInt(content);
                        elevator.addInnerRequest(floor);
                    }
                }
            }

            // 开始电梯运行
            elevator.start();
        } catch (InputMismatchException e) {
            System.out.println("输入格式错误,请输入有效的整数。");
        } catch (IllegalArgumentException e) {
            System.out.println("输入错误: " + e.getMessage());
        } finally {
            scanner.close();
        }
    }
}

输入

点击查看代码
1
20
<3,UP>
<5>
<6,DOWN>
<7>
<3>
end

输出

点击查看代码
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door

以下是对该 Elevator 类中各个方法的分析:

构造方法 Elevator(int minFloor, int maxFloor)
功能:初始化电梯对象的基本属性。设置最小楼层 minFloor 、最大楼层 maxFloor ,将当前楼层 currentFloor 初始化为 1 ,方向 direction 初始化为 IDLE ,状态 state 初始化为 STOPPED ,并初始化内部请求队列 innerRequests 、上行外部请求队列 outerRequestsUp 和下行外部请求队列 outerRequestsDown 。
参数:
minFloor :电梯可到达的最小楼层。
maxFloor :电梯可到达的最大楼层。
异常处理:如果 minFloor 大于 maxFloor ,抛出 IllegalArgumentException 异常,提示 “最小楼层不能大于最大楼层” ,以保证电梯楼层范围的合理性。

start() 方法
功能:启动电梯运行,使电梯不断检查并处理请求队列中的请求,直到所有请求队列为空。当电梯处于停止状态且有请求时,确定运行方向并开始移动,持续处理请求直至无请求可处理,最后将电梯状态设为停止,方向设为 IDLE 。
逻辑:通过 while 循环,只要内部请求队列 innerRequests 、上行外部请求队列 outerRequestsUp 或下行外部请求队列 outerRequestsDown 不为空,就持续运行。在电梯停止状态下,调用 determineDirection() 确定方向,将状态设为移动,然后调用 processRequests() 处理请求。

isAllRequestsEmpty() 方法
功能:判断所有请求队列(内部请求队列、上行外部请求队列和下行外部请求队列 )是否都为空。
逻辑:通过 && 逻辑与操作,判断 innerRequests.isEmpty() 、outerRequestsUp.isEmpty() 和 outerRequestsDown.isEmpty() 这三个条件是否同时满足,若都为空则返回 true ,否则返回 false 。

determineDirection() 方法
功能:确定电梯的运行方向。综合考虑外部上下行请求队列以及内部请求队列的情况,根据不同条件设置电梯的运行方向为 UP(上行)、DOWN(下行)或 IDLE(静止)。
逻辑:
若上行外部请求队列 outerRequestsUp 不为空,且当前方向不是 DOWN 或者下行外部请求队列 outerRequestsDown 为空,则将方向设为 UP 。
若下行外部请求队列 outerRequestsDown 不为空,且当前方向不是 UP 或者上行外部请求队列 outerRequestsUp 为空,则将方向设为 DOWN 。
若内部请求队列 innerRequests 不为空,根据队头请求楼层与当前楼层的大小关系确定方向:队头请求楼层大于当前楼层,设为 UP ;小于当前楼层,设为 DOWN 。
若上述条件都不满足,即所有请求队列都为空或不满足方向切换条件,则将方向设为 IDLE 。

processRequests() 方法
功能:根据电梯当前的运行方向,调用相应的方法处理请求。如果方向为 IDLE ,则重新确定方向并处理请求。
逻辑:根据 direction 的值进行判断,若为 UP ,调用 processUpRequests() 处理上行请求;若为 DOWN ,调用 processDownRequests() 处理下行请求;若为 IDLE ,调用 determineDirection() 重新确定方向,若确定后的方向不为 IDLE ,则将电梯状态设为移动并再次调用自身处理请求。

processUpRequests() 方法
功能:处理电梯上行方向的请求,包括外部上行请求和内部上行请求。当上行方向请求处理完毕后,检查下行方向是否有请求,若有则切换方向处理下行请求,否则将电梯状态设为停止,方向设为 IDLE 。
逻辑:
分别调用 processOuterRequests(outerRequestsUp, Direction.UP) 和 processInnerRequests(Direction.UP) 处理外部和内部的上行请求,并记录是否处理了请求。
若上行方向的外部和内部请求都没有处理(即都为空 ),检查下行外部请求队列 outerRequestsDown 是否为空,若不为空,将方向设为 DOWN ,调用 processDownRequests() 处理下行请求;若为空,将电梯状态设为停止,方向设为 IDLE 。

processDownRequests() 方法
功能:与 processUpRequests() 类似,处理电梯下行方向的请求,包括外部下行请求和内部下行请求。当下行方向请求处理完毕后,检查上行方向是否有请求,若有则切换方向处理上行请求,否则将电梯状态设为停止,方向设为 IDLE 。
逻辑:
分别调用 processOuterRequests(outerRequestsDown, Direction.DOWN) 和 processInnerRequests(Direction.DOWN) 处理外部和内部的下行请求,并记录是否处理了请求。
若下行方向的外部和内部请求都没有处理(即都为空 ),检查上行外部请求队列 outerRequestsUp 是否为空,若不为空,将方向设为 UP ,调用 processUpRequests() 处理上行请求;若为空,将电梯状态设为停止,方向设为 IDLE 。

processOuterRequests(Queue requestQueue, Direction currentDirection) 方法
功能:处理外部请求队列中的请求。遍历外部请求队列,若请求的方向与当前电梯运行方向一致,则调用 moveToFloor(request.floor) 移动电梯到该请求楼层,并标记有请求被处理。
参数:
requestQueue :要处理的外部请求队列(可以是上行或下行外部请求队列 )。
currentDirection :当前电梯的运行方向。
返回值:若处理了请求(即队列中有与当前方向一致的请求 ),返回 true ;否则返回 false 。
processInnerRequests(Direction currentDirection) 方法
功能:处理内部请求队列中的请求。遍历内部请求队列,若请求楼层符合当前电梯运行方向(上行时请求楼层大于当前楼层,下行时请求楼层小于当前楼层 ),则调用 moveToFloor(requestedFloor) 移动电梯到该请求楼层,并从队列中移除该请求,同时标记有请求被处理。
参数:currentDirection ,当前电梯的运行方向。
返回值:若处理了请求(即队列中有符合当前方向的请求 ),返回 true ;否则返回 false 。

moveToFloor(int floor) 方法
功能:模拟电梯移动到指定楼层的过程。根据目标楼层与当前楼层的大小关系,改变电梯方向并逐步增减当前楼层,同时输出当前楼层和方向信息。到达目标楼层后,调用 openAndCloseDoor(floor) 打开和关闭电梯门。
参数:floor ,要到达的目标楼层。
逻辑:
若目标楼层大于当前楼层,将方向设为 UP ,通过 while 循环使当前楼层递增,直到到达目标楼层,并在每次递增时输出当前楼层和方向。
若目标楼层小于当前楼层,将方向设为 DOWN ,通过 while 循环使当前楼层递减,直到到达目标楼层,并在每次递减时输出当前楼层和方向。
到达目标楼层后,调用 openAndCloseDoor(floor) 执行开门和关门操作。

openAndCloseDoor(int floor) 方法
功能:模拟电梯在到达指定楼层时打开和关闭电梯门的操作,通过输出语句提示门的开关状态。
参数:floor ,电梯到达的楼层。

addOuterRequest(int floor, Direction direction) 方法
功能:处理外部请求的添加。若请求楼层有效且方向和楼层满足相应条件(上行且楼层大于当前楼层,或下行且楼层小于当前楼层 ),则将请求添加到对应的外部请求队列(上行或下行 )中;否则输出错误提示 “无效的楼层请求: [楼层号]” 。
参数:
floor :请求的楼层。
direction :请求的方向(UP 或 DOWN )。

addInnerRequest(int floor) 方法
功能:处理内部请求的添加。若请求楼层在有效范围内(大于等于最小楼层且小于等于最大楼层 ),则将请求添加到内部请求队列 innerRequests 中;否则输出错误提示 “无效的楼层请求: [楼层号]” 。
参数:floor ,请求的楼层。

isValidFloor(int floor) 方法
功能:检查给定楼层是否在电梯可到达的有效楼层范围内(大于等于最小楼层且小于等于最大楼层 )。
参数:floor ,要检查的楼层。
返回值:若楼层有效,返回 true ;否则返回 false 。

main(String[] args) 方法
功能:程序的入口点,用于测试电梯运行。通过 Scanner 从控制台获取用户输入,包括最小和最大楼层,以及一系列的电梯请求(内部请求和外部请求 ),创建电梯对象并添加请求,最后启动电梯运行。同时,对可能出现的输入格式错误(InputMismatchException )和非法参数错误(IllegalArgumentException )进行捕获和提示处理,在程序结束时关闭 Scanner 。

从输出结果来看,对比正确输出和错误输出,发现错误输出中缺少了 Current Floor: 1 Direction: UP 这一行,并且在下行到楼层 6 时没有打开和关闭门的操作。可能是在电梯初始化或者请求处理的起始逻辑,以及下行请求处理逻辑中存在问题。

对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客请求类、队列类以及控制类,具体设计可参考如下类图。
电梯迭代1类图.png
电梯运行规则与前阶段单类设计相同,但要处理如下情况:

单部电梯调度程序(类设计)

乘客请求楼层数有误,具体为高于最高楼层数或低于最低楼层数,处理方法:程序自动忽略此类输入,继续执行
乘客请求不合理,具体为输入时出现连续的相同请求,例如<3><3><3>或者<5,DOWN><5,DOWN>,处理方法:程序自动忽略相同的多余输入,继续执行,例如<3><3><3>过滤为<3>

注意:本次作业类设计必须符合如上要求(包含但不限于乘客请求类、电梯类、请求队列类及控制类,其中控制类专门负责电梯调度过程),凡是不符合类设计要求此题不得分,另外,PTA得分代码界定为第一次提交的最高分代码(因此千万不要把第一次电梯程序提交到本次题目中测试)。

输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。

电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。

输出格式:
模拟电梯的运行过程,输出方式如下:

运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door

输入样例1:

点击查看代码
1
20
<3,UP>
<5>
<6,DOWN>
<7>
<3>
end

输出样例1:

点击查看代码
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door

输入样例2:

点击查看代码
1
20
<3,UP>
<3,UP>
<5>
<5>
<5>
<6,DOWN>
<7>
<7>
<3>
<22,DOWN>
<5,DOWN>
<30>
END

输出样例2:

点击查看代码
Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Open Door # Floor 5
Close Door
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door

源码

点击查看代码
import java.util.LinkedList;
import java.util.Scanner;

public class ElevatorSystem {
    // 方向枚举
    public enum Direction {
        UP, DOWN, IDLE
    }

    // 状态枚举
    public enum State {
        MOVING, STOPPED
    }

    // 外部请求类
    public static class ExternalRequest {
        private Integer floor;
        private Direction direction;

        public ExternalRequest(Integer floor, Direction direction) {
            this.floor = floor;
            this.direction = direction;
        }

        public Integer getFloor() {
            return floor;
        }

        public Direction getDirection() {
            return direction;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ExternalRequest that = (ExternalRequest) o;
            return floor.equals(that.floor) && direction == that.direction;
        }

        @Override
        public int hashCode() {
            int result = floor.hashCode();
            result = 31 * result + direction.hashCode();
            return result;
        }
    }

    // 请求队列类
    public static class RequestQueue {
        private LinkedList<Integer> internalRequests = new LinkedList<>();
        private LinkedList<ExternalRequest> externalRequests = new LinkedList<>();

        public RequestQueue getQueueInstance() {
            return this;
        }

        public LinkedList<Integer> getInternalRequests() {
            return internalRequests;
        }

        public void setInternalRequests(LinkedList<Integer> internalRequests) {
            this.internalRequests = internalRequests;
        }

        public LinkedList<ExternalRequest> getExternalRequests() {
            return externalRequests;
        }

        public void setExternalRequests(LinkedList<ExternalRequest> externalRequests) {
            this.externalRequests = externalRequests;
        }

        public void addInternalRequest(int floor) {
            if (!internalRequests.contains(floor)) {
                internalRequests.add(floor);
            }
        }

        public void addExternalRequest(int floor, Direction direction) {
            ExternalRequest request = new ExternalRequest(floor, direction);
            if (!externalRequests.contains(request)) {
                externalRequests.add(request);
            }
        }
    }

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

        public Elevator(int minFloor, int maxFloor) {
            this.minFloor = minFloor;
            this.maxFloor = maxFloor;
            this.currentFloor = minFloor;
            this.direction = Direction.IDLE;
            this.state = State.STOPPED;
        }

        public int getCurrentFloor() {
            return currentFloor;
        }

        public void setCurrentFloor(int currentFloor) {
            this.currentFloor = currentFloor;
        }

        public Direction getDirection() {
            return direction;
        }

        public void setDirection(Direction direction) {
            this.direction = direction;
        }

        public State getState() {
            return state;
        }

        public void setState(State state) {
            this.state = state;
        }

        public int getMaxFloor() {
            return maxFloor;
        }

        public int getMinFloor() {
            return minFloor;
        }

        public boolean isValidFloor(int floor) {
            return floor >= minFloor && floor <= maxFloor;
        }
    }

    // 控制类
    public static class Controller {
        private Elevator elevator;
        private RequestQueue queue;

        public Controller(Elevator elevator, RequestQueue queue) {
            this.elevator = elevator;
            this.queue = queue;
        }

        public Elevator getElevator() {
            return elevator;
        }

        public void setElevator(Elevator elevator) {
            this.elevator = elevator;
        }

        public RequestQueue getQueue() {
            return queue;
        }

        public void setQueue(RequestQueue queue) {
            this.queue = queue;
        }

        public void processRequests() {
            while (!isAllRequestsEmpty()) {
                if (elevator.getState() == State.STOPPED) {
                    determineDirection();
                    elevator.setState(State.MOVING);
                }
                move();
            }
            elevator.setState(State.STOPPED);
            elevator.setDirection(Direction.IDLE);
        }

        private boolean isAllRequestsEmpty() {
            return queue.getInternalRequests().isEmpty() && queue.getExternalRequests().isEmpty();
        }

        private void determineDirection() {
            if (!queue.getExternalRequests().isEmpty()) {
                ExternalRequest firstExternalRequest = queue.getExternalRequests().peek();
                if (firstExternalRequest.getFloor() > elevator.getCurrentFloor()) {
                    elevator.setDirection(Direction.UP);
                } else {
                    elevator.setDirection(Direction.DOWN);
                }
            } else if (!queue.getInternalRequests().isEmpty()) {
                int firstInternalRequest = queue.getInternalRequests().peek();
                if (firstInternalRequest > elevator.getCurrentFloor()) {
                    elevator.setDirection(Direction.UP);
                } else {
                    elevator.setDirection(Direction.DOWN);
                }
            } else {
                elevator.setDirection(Direction.IDLE);
            }
        }

        private void move() {
            if (elevator.getDirection() == Direction.UP) {
                processUpRequests();
            } else if (elevator.getDirection() == Direction.DOWN) {
                processDownRequests();
            }
        }

        private void processUpRequests() {
            boolean hasExternalRequest = processExternalRequests(queue.getExternalRequests(), Direction.UP);
            boolean hasInternalRequest = processInternalRequests(queue.getInternalRequests(), Direction.UP);

            if (!hasExternalRequest &&!hasInternalRequest) {
                if (!queue.getExternalRequests().isEmpty()) {
                    determineDirection();
                    processRequests();
                } else {
                    elevator.setState(State.STOPPED);
                    elevator.setDirection(Direction.IDLE);
                }
            }
        }

        private void processDownRequests() {
            boolean hasExternalRequest = processExternalRequests(queue.getExternalRequests(), Direction.DOWN);
            boolean hasInternalRequest = processInternalRequests(queue.getInternalRequests(), Direction.DOWN);

            if (!hasExternalRequest &&!hasInternalRequest) {
                if (!queue.getExternalRequests().isEmpty()) {
                    determineDirection();
                    processRequests();
                } else {
                    elevator.setState(State.STOPPED);
                    elevator.setDirection(Direction.IDLE);
                }
            }
        }

        private boolean processExternalRequests(LinkedList<ExternalRequest> requestQueue, Direction currentDirection) {
            boolean processedRequest = false;
            java.util.Iterator<ExternalRequest> iterator = requestQueue.iterator();
            while (iterator.hasNext()) {
                ExternalRequest request = iterator.next();
                if (request.getDirection() == currentDirection) {
                    if (elevator.isValidFloor(request.getFloor())) {
                        moveToFloor(request.getFloor());
                        iterator.remove();
                        processedRequest = true;
                    }
                }
            }
            return processedRequest;
        }

        private boolean processInternalRequests(LinkedList<Integer> requestQueue, Direction currentDirection) {
            boolean processedRequest = false;
            java.util.Iterator<Integer> iterator = requestQueue.iterator();
            while (iterator.hasNext()) {
                int requestedFloor = iterator.next();
                if (elevator.isValidFloor(requestedFloor)) {
                    if ((currentDirection == Direction.UP && requestedFloor > elevator.getCurrentFloor()) ||
                            (currentDirection == Direction.DOWN && requestedFloor < elevator.getCurrentFloor())) {
                        moveToFloor(requestedFloor);
                        iterator.remove();
                        processedRequest = true;
                    }
                }
            }
            return processedRequest;
        }

        private void moveToFloor(int floor) {
            if (elevator.getCurrentFloor() != floor) {
                if (floor > elevator.getCurrentFloor()) {
                    elevator.setDirection(Direction.UP);
                    while (elevator.getCurrentFloor() < floor) {
                        elevator.setCurrentFloor(elevator.getCurrentFloor() + 1);
                        System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getDirection());
                    }
                } else {
                    elevator.setDirection(Direction.DOWN);
                    while (elevator.getCurrentFloor() > floor) {
                        elevator.setCurrentFloor(elevator.getCurrentFloor() - 1);
                        System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getDirection());
                    }
                }
            }
            openAndCloseDoor(floor);
        }

        private void openAndCloseDoor(int floor) {
            System.out.println("Open Door # Floor " + floor);
            System.out.println("Close Door");
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int minFloor = scanner.nextInt();
        int maxFloor = scanner.nextInt();
        scanner.nextLine();

        Elevator elevator = new Elevator(minFloor, maxFloor);
        RequestQueue queue = new RequestQueue();
        Controller controller = new Controller(elevator, queue);

        String line;
        while (!(line = scanner.nextLine()).equalsIgnoreCase("end")) {
            if (line.startsWith("<") && line.endsWith(">")) {
                String content = line.substring(1, line.length() - 1);
                if (content.contains(",")) {
                    String[] parts = content.split(",");
                    int floor = Integer.parseInt(parts[0]);
                    Direction direction = Direction.valueOf(parts[1]);
                    if (elevator.isValidFloor(floor)) {
                        queue.addExternalRequest(floor, direction);
                    }
                } else {
                    int floor = Integer.parseInt(content);
                    if (elevator.isValidFloor(floor)) {
                        queue.addInternalRequest(floor);
                    }
                }
            }
        }

        controller.processRequests();
        scanner.close();
    }
}

输入1:

点击查看代码
1
20
<3,UP>
<5>
<6,DOWN>
<7>
<3>
end

输出1:

点击查看代码
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door

输入2:

点击查看代码
1
20
<3,UP>
<3,UP>
<5>
<5>
<5>
<6,DOWN>
<7>
<7>
<3>
<22,DOWN>
<5,DOWN>
<30>
END

输出2:

点击查看代码
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Open Door # Floor 5
Close Door
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door

遇到的问题

1.类职责划分难题:在遵循单一职责原则将代码从单类设计转变为多类设计时,准确划分电梯类、乘客请求类、队列类以及控制类的职责存在困难。例如,在设计控制类时,难以清晰界定其与电梯类和队列类的功能边界,导致部分逻辑在多个类中重复出现,增加了代码的耦合度。

2.请求过滤处理不当:处理乘客请求时,对于楼层数有误(高于最高楼层数或低于最低楼层数)以及连续相同请求的过滤逻辑不够完善。在原代码中,没有对请求进行有效的去重和合法性检查,导致程序可能会处理无效请求,影响电梯调度的准确性。

3.LinkedList 删除操作引发的索引越界:在使用 LinkedList 存储请求队列并进行删除操作时,由于没有正确使用迭代器,直接通过索引删除元素,导致出现 IndexOutOfBoundsException 异常。例如,在 processExternalRequests 和 processInternalRequests 方法中,原代码使用 remove 方法传入索引删除元素,当列表大小发生变化时,索引可能超出有效范围。

4.代码复用性和可维护性不足:代码结构设计不够合理,部分功能模块的复用性较差。例如,在处理上行和下行请求的逻辑中,有很多重复的代码,这使得代码冗长,增加了维护和调试的难度。当需求发生变更时,需要对多个地方进行修改,容易引入新的错误。

踩坑心得

1.类设计原则的实践挑战:虽然单一职责原则的概念易于理解,但在实际应用中,要根据具体的业务逻辑准确划分类的职责并非易事。尤其是在处理复杂的电梯调度问题时,不同类之间的交互和依赖关系需要仔细设计,否则容易导致类功能混杂,影响代码的可维护性和可扩展性。

2.输入处理的严谨性:在处理用户输入时,必须充分考虑各种可能的异常情况,如无效楼层请求和连续相同请求。任何对边界条件的遗漏都可能导致程序出现错误,影响系统的正常运行。这也提醒我们在编程过程中要养成严谨的思维习惯,对输入进行严格的验证和过滤。

3.数据结构操作的正确性:在使用数据结构时,要深入理解其操作方法和特性。对于 LinkedList 这样的动态列表,在进行删除操作时,必须使用迭代器来避免索引越界问题。这说明在编程过程中,不仅要掌握数据结构的基本使用,还要注意其在不同场景下的正确应用。

4.代码质量的重要性:初期编写代码时,往往只注重功能的实现,而忽视了代码的复用性和可维护性。随着需求的不断变化和代码的不断迭代,代码质量问题逐渐凸显。高质量的代码应该具有良好的结构和清晰的逻辑,便于复用和维护,这对于长期的项目开发至关重要。

改进建议

1.加强类设计能力:在开始编码之前,仔细分析问题的业务逻辑,绘制类图和流程图,明确各个类的职责和相互关系。可以参考一些优秀的设计模式和案例,学习如何合理划分类的功能,提高类设计的准确性和合理性。

2.完善输入处理逻辑:在处理用户输入时,增加详细的验证和过滤机制。对于无效楼层请求和连续相同请求,要在输入阶段进行严格检查,并及时给出提示信息。可以封装输入处理的逻辑为独立的方法或类,提高代码的复用性和可维护性。
正确使用数据结构:在使用数据结构时,要深入学习其操作方法和特性,根据具体的业务需求选择合适的数据结构。对于需要频繁删除元素的场景,优先使用迭代器进行操作,避免出现索引越界等问题。同时,可以编写单元测试来验证数据结构操作的正确性。

3.提高代码复用性和可维护性:在编写代码时,要养成良好的代码习惯,及时提取重复的代码逻辑,封装成独立的方法或类。使用设计模式来优化代码结构,降低代码的耦合度。在每次需求变更时,对代码进行合理的重构,确保代码的质量和可维护性。

总结

通过本次单部电梯调度程序的开发,我对面向对象编程中的类设计原则有了更深入的理解,掌握了如何根据单一职责原则将复杂的业务逻辑划分为不同的类,提高了代码的可维护性和可扩展性。在处理用户输入和请求过滤方面,我认识到了严谨性的重要性,学会了如何对输入进行严格的验证和过滤,避免程序出现错误。同时,我也深刻体会到了代码质量对于项目开发的重要性,明白了在编程过程中要注重代码的复用性和可维护性。

在这个过程中,我遇到了许多问题,但通过不断地调试和优化,最终解决了这些问题,提高了自己的编程能力。每次遇到问题都是一次学习和成长的机会,我会将这些经验教训应用到今后的编程中,不断改进自己的代码质量和编程思维。希望在后续的学习中,能够有更多的实践机会,进一步巩固和拓展所学的知识。同时,也希望能有更多的测试用例,帮助我更全面地发现代码中的问题,提高程序的稳定性和可靠性。

posted on 2025-04-20 19:05  23201836-高歌  阅读(142)  评论(0)    收藏  举报

导航