题目
单部电梯调度程序
设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为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:
点击查看代码- 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>
- <5>
- <6,DOWN>
- <7>
- <3>
- 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
- 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.提高代码复用性和可维护性:在编写代码时,要养成良好的代码习惯,及时提取重复的代码逻辑,封装成独立的方法或类。使用设计模式来优化代码结构,降低代码的耦合度。在每次需求变更时,对代码进行合理的重构,确保代码的质量和可维护性。
总结
通过本次单部电梯调度程序的开发,我对面向对象编程中的类设计原则有了更深入的理解,掌握了如何根据单一职责原则将复杂的业务逻辑划分为不同的类,提高了代码的可维护性和可扩展性。在处理用户输入和请求过滤方面,我认识到了严谨性的重要性,学会了如何对输入进行严格的验证和过滤,避免程序出现错误。同时,我也深刻体会到了代码质量对于项目开发的重要性,明白了在编程过程中要注重代码的复用性和可维护性。
在这个过程中,我遇到了许多问题,但通过不断地调试和优化,最终解决了这些问题,提高了自己的编程能力。每次遇到问题都是一次学习和成长的机会,我会将这些经验教训应用到今后的编程中,不断改进自己的代码质量和编程思维。希望在后续的学习中,能够有更多的实践机会,进一步巩固和拓展所学的知识。同时,也希望能有更多的测试用例,帮助我更全面地发现代码中的问题,提高程序的稳定性和可靠性。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |