南昌航空大学-软件学院-24201625-梁少康-第一次blog作业

1.前言

刚完成前三次JAVA迭代作业,我心里有不少疑惑。为什么有些题目我反复出错?怎样才能更高效地完成作业,掌握知识?相信很多同学和我一样,在作业中有着类似的困扰。这个博客,便是我探寻答案的地方。我会把完成作业时遇到的困惑都摆出来,深入思考背后的原因,尝试寻找解决办法。希望在这个过程中,我们能一起解开作业带来的谜团,找到更适合自己的学习方式,让作业不再是负担,而是助力我们进步的工具 。
1.关于题量
对于三次题目集来说,随着最后一道题难度的提高,题目数量也从5个题目缩减到3个题目,总的代码量保持在了一个适当的水平。如果每次都按照正常的速度去完成都是能够写完的。
2.关于知识点
在知识点上前三次大作业的题集都是在帮助我们熟悉java的基础语法,巩固和加深语法的使用规范。同时题目都会提供相应的类图,能帮助我们快速进行代码的实现,熟悉对象设计的基本过程。
3.关于难度
这几次题目集的除最后一题外的题目都只涉及到简单的算法设计,主要是类之间的关系设计,所以相对比较简单,而最后一题却有着比较复杂的算法设计,在不知道详细测试点和缺少充足样例输入输出的情况下,想要得到题目正确结果是有一定难度的;

2.设计与分析

2.1 OPP-1: 电梯调度程序1

2.1.1 题目

7-5 NCHU_单部电梯调度程序
分数 70
中等
作者 段喜龙
单位 南昌航空大学
设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。
请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求),具体运行规则详见输入输出样例。

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

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

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

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

代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
栈限制
8192 KB

2.1.2 源码

import java.util.*;

class ExternalRequests {
    private int floor;
    private String direction;

    public ExternalRequests(int floor, String direction) {
        this.floor = floor;
        this.direction = direction.toUpperCase();
    }

    public int getFloor() {
        return floor;
    }

    public String getDirection() {
        return direction;
    }
}

class Elevator {
    private int minFloor;
    private int maxFloor;
    private int currentFloor;
    private String currentDirection = "UP";
    private ArrayList<Integer> internalRequests;
    private ArrayList<ExternalRequests> externalRequests;

    public Elevator(int minFloor, int maxFloor) {
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.currentFloor = minFloor;
        this.internalRequests = new ArrayList<>();
        this.externalRequests = new ArrayList<>();
    }

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

    public void addExternalRequest(int floor, String direction) {
        externalRequests.add(new ExternalRequests(floor, direction));
    }

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

    public List<ExternalRequests> getExternalRequests() {
        return externalRequests;
    }
    
    public void processRequests() {
        System.out.println("Current Floor: " + currentFloor + " Direction: " + currentDirection);
        while (!internalRequests.isEmpty() || !externalRequests.isEmpty()) {
            move();
        }
    }

    private void determineDirection() {
        if (!internalRequests.isEmpty() && !externalRequests.isEmpty()) {
            int infloor = internalRequests.get(0);
            int exfloor = externalRequests.get(0).getFloor();
            
            if ("UP".equals(currentDirection)) {
                if (infloor < currentFloor && exfloor < currentFloor) {
                    currentDirection = "DOWN";
                }
            }
            
            if ("DOWN".equals(currentDirection)) {
                if (infloor > currentFloor && exfloor > currentFloor) {
                    currentDirection = "UP";
                }
            }
        } else if (!internalRequests.isEmpty()) {
            int infloor = internalRequests.get(0);
            if ("UP".equals(currentDirection) && infloor < currentFloor) {
                currentDirection = "DOWN";
            } else if ("DOWN".equals(currentDirection) && infloor > currentFloor) {
                currentDirection = "UP";
            }
        } else if (!externalRequests.isEmpty()) {
            int exfloor = externalRequests.get(0).getFloor();
            if ("UP".equals(currentDirection) && exfloor < currentFloor) {
                currentDirection = "DOWN";
            } else if ("DOWN".equals(currentDirection) && exfloor > currentFloor) {
                currentDirection = "UP";
            }
        }
    }
    
    private void move() {
        if ("UP".equals(currentDirection)) {
            currentFloor++;
        } else if ("DOWN".equals(currentDirection)) {
            currentFloor--;
        }

        System.out.println("Current Floor: " + currentFloor + " Direction: " + currentDirection);

        if (shouldStop()) {
            System.out.println("Open Door # Floor " + currentFloor);
            System.out.println("Close Door");
        }
        determineDirection();
    }

    private boolean shouldStop() {
        boolean inis = inStop();
        boolean exis = exStop();
        return inis || exis;
    }

    private boolean inStop() {
        if (!internalRequests.isEmpty() && internalRequests.get(0) == currentFloor) {
            internalRequests.remove(0);
            return true;
        }
        return false;
    }

    private boolean exStop() {
        if (!externalRequests.isEmpty()) {
            ExternalRequests firstExternal = externalRequests.get(0);
            if (firstExternal.getFloor() == currentFloor) {
                if (!internalRequests.isEmpty()) {
                    if (firstExternal.getDirection().equals(currentDirection) ||
                        ("UP".equals(firstExternal.getDirection()) && internalRequests.get(0) > firstExternal.getFloor()) ||
                        ("DOWN".equals(firstExternal.getDirection()) && internalRequests.get(0) < firstExternal.getFloor())) {
                        currentDirection = firstExternal.getDirection();
                        externalRequests.remove(0);
                        return true;
                    }
                } else {
                    externalRequests.remove(0);
                    return true;
                }
            }
        }
        return false;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String data;
        int floor;
        String direction;
        int minFloor = Integer.parseInt(input.next());
        int maxFloor = Integer.parseInt(input.next());
        Elevator elevator = new Elevator(minFloor, maxFloor);

        while (!(data = input.next()).equalsIgnoreCase("End")) {
            if (data.contains(",")) {
                if (!data.matches("<\\d+,\\s*(UP|DOWN)>")) {
                    System.out.println("Wrong Format");
                    continue;
                }
                String[] parts = data.replaceAll("[<>]", "").split(",");
                floor = Integer.parseInt(parts[0].trim());
                direction = parts[1].trim().toUpperCase();
                elevator.addExternalRequest(floor, direction);
            } else {
                if (!data.matches("<\\d+>")) {
                    System.out.println("Wrong Format");
                    continue;
                }
                floor = Integer.parseInt(data.replaceAll("[<>]", ""));
                elevator.addInternalRequest(floor);
            }
        }

        while (!elevator.getInternalRequests().isEmpty() || !elevator.getExternalRequests().isEmpty()) {
            elevator.processRequests();
        }
        input.close();
    }
}

2.1.3 设计与分析

image
image

设计总结

1.类与职责

  • ExternalRequests类:负责封装外部电梯请求信息,包含楼层(floor)和方向(direction)属性,提供获取属性的方法,实现了对外部请求数据的简单管理。
  • Elevator类:是电梯功能的核心类,管理电梯的运行状态,如最小楼层(minFloor)、最大楼层(maxFloor)、当前楼层(currentFloor)、运行方向(currentDirection)等。同时处理内部请求(internalRequests)和外部请求(externalRequests),包含添加请求、处理请求、判断运行方向、移动电梯等方法,实现电梯运行逻辑。
  • Main类:作为程序入口,负责读取用户输入,根据输入格式判断是内部请求还是外部请求,并将其添加到电梯对象中,最后调用电梯的请求处理方法,协调程序的整体流程。

2.数据结构使用

  • 使用ArrayList存储内部请求(internalRequests)和外部请求(externalRequests),方便动态添加和删除请求,符合请求数量不确定的实际场景。

3.交互设计

  • 通过控制台输入获取电梯运行相关请求信息,输入格式通过正则表达式进行校验,保证输入数据的格式正确性。当输入"End"时停止接收请求并开始处理已有的请求。

分析总结

1.方法复杂度:从代码分析结果看,Elevator.determineDirection()方法复杂度较高(19, 21, 5, 14 ),该方法中包含多个条件判断和逻辑分支,用于综合内部和外部请求以及当前电梯方向来确定下一步运行方向,逻辑较为复杂。Main.main()方法复杂度也较高(8, 23, 5, 21 ),涉及输入读取、格式校验、请求添加以及请求处理的循环逻辑,代码行数和逻辑分支较多。
2.运行逻辑:电梯运行逻辑基于请求队列,在处理请求时,先根据当前方向以及内外部请求情况判断是否需要改变方向,然后移动电梯,在到达目标楼层时判断是否需要停靠,停靠逻辑综合考虑内外部请求,保证电梯运行符合实际需求。
3.可扩展性:当前设计对于基本的电梯请求处理功能实现较为清晰,但如果要扩展功能,如添加更多类型的请求(如特殊优先级请求)、优化调度算法(如考虑更多电梯运行效率指标)等,可能需要对Elevator类的请求处理逻辑和数据结构进行调整和扩展。

2.1.4 踩坑心得

1.在开始代码设计之前一定要确认好类与类之间的关系,做好类的设计,不然在后续增改内容时很容易大面积修改代码,或者增添代码。这样不仅增添了自己的工作量,还会使代码的质量下降;
2.题目设置end不区分大小写一定要注意进行统一大写或者小写转化,不然很容易使你的程序发生编译错误,这也是题目很细节的一个小坑;
3.在进行代码实现前一定要好好揣摩题目的输入和输出样例,这样才能清楚程序底层的算法逻辑,不然按照自己的思路的话,你会发现你的输出结果和输出样例天差地别。

2.1.5 改进建议

1.部分类的方法代码长度过长,不匹配单一职责的原则;
2.类与类之间的引用应该符合规范。

2.2 OPP-2: 电梯调度程序2

2.2.1 题目

对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(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

代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
栈限制
8192 KB

2.2.2 源码

import java.util.*;

class ExternalRequests {
    private int floor;
    private String direction;

    public ExternalRequests(int floor, String direction) {
        this.floor = floor;
        this.direction = direction.toUpperCase();
    }

    public int getFloor() {
        return floor;
    }

    public String getDirection() {
        return direction;
    }

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

    @Override
    public int hashCode() {
        return Objects.hash(floor, direction);
    }
}

class Elevator {
    private int minFloor;
    private int maxFloor;
    private int currentFloor;
    private String currentDirection = "UP";

    public Elevator(int minFloor, int maxFloor) {
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.currentFloor = minFloor;
    }

    public int getCurrentFloor() {
        return currentFloor;
    }

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

    public String getCurrentDirection() {
        return currentDirection;
    }

    public void setCurrentDirection(String currentDirection) {
        this.currentDirection = currentDirection.toUpperCase();
    }

    public int getMinFloor() {
        return minFloor;
    }

    public void setMinFloor(int minFloor) {
        this.minFloor = minFloor;
    }

    public int getMaxFloor() {
        return maxFloor;
    }

    public void setMaxFloor(int maxFloor) {
        this.maxFloor = maxFloor;
    }
}

class RequestQueue {
    private ArrayList<Integer> internalRequests;
    private ArrayList<ExternalRequests> externalRequests;
    private Integer lastInternalRequest = null;
    private ExternalRequests lastExternalRequest = null;

    public RequestQueue() {
        this.internalRequests = new ArrayList<>();
        this.externalRequests = new ArrayList<>();
    }

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

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

    public ArrayList<ExternalRequests> getExternalRequests() {
        return externalRequests;
    }

    public void setExternalRequests(ArrayList<ExternalRequests> externalRequests) {
        this.externalRequests = externalRequests;
    }

    public void addInternalRequest(int floor, int minFloor, int maxFloor) {
        if (floor < minFloor || floor > maxFloor) {
            return;
        }
        if (lastInternalRequest != null && lastInternalRequest == floor) {
            return;
        }
        internalRequests.add(floor);
        lastInternalRequest = floor;
    }

    public void addExternalRequest(int floor, String direction, int minFloor, int maxFloor) {
        if (floor < minFloor || floor > maxFloor) {
            return;
        }
        ExternalRequests newRequest = new ExternalRequests(floor, direction);
        if (lastExternalRequest != null && lastExternalRequest.equals(newRequest)) {
            return;
        }
        externalRequests.add(newRequest);
        lastExternalRequest = newRequest;
    }
}

class Controller {
    private Elevator elevator;
    private RequestQueue requestQueue;

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

    public Elevator getElevator() {
        return elevator;
    }

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

    public RequestQueue getQueue() {
        return requestQueue;
    }

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

    public void processRequests() {
        System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getCurrentDirection());
        while (!requestQueue.getInternalRequests().isEmpty() || !requestQueue.getExternalRequests().isEmpty()) {
            move();
        }
    }

    private void determineDirection() {
        if (!requestQueue.getInternalRequests().isEmpty() && !requestQueue.getExternalRequests().isEmpty()) {
            int infloor = requestQueue.getInternalRequests().get(0);
            int exfloor = requestQueue.getExternalRequests().get(0).getFloor();

            if ("UP".equals(elevator.getCurrentDirection())) {
                if (infloor < elevator.getCurrentFloor() && exfloor < elevator.getCurrentFloor()) {
                    elevator.setCurrentDirection("DOWN");
                }
            }

            if ("DOWN".equals(elevator.getCurrentDirection())) {
                if (infloor > elevator.getCurrentFloor() && exfloor > elevator.getCurrentFloor()) {
                    elevator.setCurrentDirection("UP");
                }
            }
        } else if (!requestQueue.getInternalRequests().isEmpty()) {
            int infloor = requestQueue.getInternalRequests().get(0);
            if ("UP".equals(elevator.getCurrentDirection()) && infloor < elevator.getCurrentFloor()) {
                elevator.setCurrentDirection("DOWN");
            } else if ("DOWN".equals(elevator.getCurrentDirection()) && infloor > elevator.getCurrentFloor()) {
                elevator.setCurrentDirection("UP");
            }
        } else if (!requestQueue.getExternalRequests().isEmpty()) {
            int exfloor = requestQueue.getExternalRequests().get(0).getFloor();
            if ("UP".equals(elevator.getCurrentDirection()) && exfloor < elevator.getCurrentFloor()) {
                elevator.setCurrentDirection("DOWN");
            } else if ("DOWN".equals(elevator.getCurrentDirection()) && exfloor > elevator.getCurrentFloor()) {
                elevator.setCurrentDirection("UP");
            }
        }
    }

    private void move() {
        if ("UP".equals(elevator.getCurrentDirection())) {
            elevator.setCurrentFloor(elevator.getCurrentFloor() + 1);
        } else if ("DOWN".equals(elevator.getCurrentDirection())) {
            elevator.setCurrentFloor(elevator.getCurrentFloor() - 1);
        }

        System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getCurrentDirection());

        if (shouldStop()) {
            System.out.println("Open Door # Floor " + elevator.getCurrentFloor());
            System.out.println("Close Door");
        }
        determineDirection();
    }

    private boolean shouldStop() {
        boolean inis = inStop();
        boolean exis = exStop();
        return inis || exis;
    }

    private boolean inStop() {
        if (!requestQueue.getInternalRequests().isEmpty() && requestQueue.getInternalRequests().get(0) == elevator.getCurrentFloor()) {
            requestQueue.getInternalRequests().remove(0);
            return true;
        }
        return false;
    }

    private boolean exStop() {
        if (!requestQueue.getExternalRequests().isEmpty()) {
            ExternalRequests firstExternal = requestQueue.getExternalRequests().get(0);
            if (firstExternal.getFloor() == elevator.getCurrentFloor()) {
                if (!requestQueue.getInternalRequests().isEmpty()) {
                    if (firstExternal.getDirection().equals(elevator.getCurrentDirection()) ||
                            ("UP".equals(firstExternal.getDirection()) && requestQueue.getInternalRequests().get(0) > firstExternal.getFloor()) ||
                            ("DOWN".equals(firstExternal.getDirection()) && requestQueue.getInternalRequests().get(0) < firstExternal.getFloor())) {
                        elevator.setCurrentDirection(firstExternal.getDirection());
                        requestQueue.getExternalRequests().remove(0);
                        return true;
                    }
                } else {
                    requestQueue.getExternalRequests().remove(0);
                    return true;
                }
            }
        }
        return false;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String data;
        int floor;
        String direction;
        int minFloor = Integer.parseInt(input.next());
        int maxFloor = Integer.parseInt(input.next());
        Elevator elevator = new Elevator(minFloor, maxFloor);
        RequestQueue requestQueue = new RequestQueue();
        Controller controller = new Controller(elevator, requestQueue);

        while (!(data = input.next()).equalsIgnoreCase("End")) {
            if (data.contains(",")) {
                if (!data.matches("<\\d+,\\s*(UP|DOWN)>")) {
                    System.out.println("Wrong Format");
                    continue;
                }
                String[] parts = data.replaceAll("[<>]", "").split(",");
                floor = Integer.parseInt(parts[0].trim());
                direction = parts[1].trim().toUpperCase();
                requestQueue.addExternalRequest(floor, direction, minFloor, maxFloor);
            } else {
                if (!data.matches("<\\d+>")) {
                    System.out.println("Wrong Format");
                    continue;
                }
                floor = Integer.parseInt(data.replaceAll("[<>]", ""));
                requestQueue.addInternalRequest(floor, minFloor, maxFloor);
            }
        }

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

2.2.3 设计与分析

image
image

设计总结

1.类的职责划分

  • ExternalRequests类:封装外部电梯请求信息,包括楼层和方向,提供属性访问方法,同时重写了equals和hashCode方法,便于在集合中进行元素比较和存储,保证请求的唯一性判断。
  • Elevator类:管理电梯的状态,如当前楼层、运行方向、最小楼层和最大楼层,通过一系列的getter和setter方法提供对电梯状态的访问和修改,实现了对电梯基本属性的封装和管理。
  • RequestQueue类:负责管理内部和外部请求队列,提供添加请求的方法,并通过记录上一个内部和外部请求,避免重复添加相同请求,保证请求队列的有效性和准确性。
  • Controller类:作为核心控制类,协调Elevator和RequestQueue,实现电梯运行逻辑,包括处理请求、确定运行方向、控制电梯移动以及判断是否停靠等功能,是电梯运行控制的核心逻辑所在。
  • Main类:程序入口,负责读取用户输入,根据输入格式判断是内部还是外部请求,并添加到相应队列,最后调用Controller处理请求,协调整个程序的运行流程。
    2.数据结构运用
  • 使用ArrayList存储内部请求(internalRequests)和外部请求(externalRequests),利用其动态数组特性,方便在运行过程中灵活添加和删除请求,满足电梯请求数量不确定的实际需求。
    交互设计
  • 通过控制台接收用户输入,采用特定格式(内部请求<楼层>,外部请求<楼层, 方向>),并使用正则表达式对输入格式进行校验,确保输入数据的规范性。当输入"End"时,停止接收请求并开始处理已接收的请求。

分析总结

1.方法复杂度:从代码分析结果来看,Controller.determineDirection()方法复杂度较高(19, 21, 5, 38 ),该方法中存在大量的条件判断和分支逻辑,用于综合内外部请求和当前电梯方向来确定下一步运行方向,复杂的逻辑嵌套导致代码可读性和可维护性降低。其他如Controller.exStop()等方法复杂度也相对较高,内部逻辑存在较多条件判断和业务处理,需要进一步优化以提高代码质量。
2.运行逻辑:电梯运行逻辑围绕请求队列展开,Controller类先根据请求情况确定电梯运行方向,然后控制电梯移动,在移动过程中根据内外部请求判断是否需要停靠,整体逻辑基本实现了电梯运行的基本功能,但在处理复杂请求场景(如多个同向外部请求不同楼层等)时,可能需要进一步优化停靠和运行方向判断逻辑。
3.可扩展性:当前设计在一定程度上实现了模块化,各个类职责明确。然而,随着需求的扩展,如增加电梯数量、引入更复杂的调度算法、考虑电梯载重等因素时,现有的代码结构可能需要较大调整。例如,可以进一步抽象出电梯调度策略接口,让不同的策略实现类处理不同的调度规则,提高系统的可扩展性和灵活性。

2.2.4 踩坑心得

1.注意具体看清题目,它不仅仅是更改增加类那么简单,它额外增添了过滤不合理和重复请求的操作,不要忘记增加相应的功能;
2.类数量的增加一定要搞清楚类与类之间的关系,新增方法放在哪一个类上,注意思考如何设计才能使代码修改得更加合理,特别是其中Controller与其他类对应的关系,减少代码的重复;
3.一定注意一次性读入的时候在读取中就对输入数据进行过滤不合理数据,否则当你全部读取储存到数据再来剔除的话会增加代码的复杂度,增加不必要的代码实现。

2.2.5 改进建议

1.在get(0)之前增加判空处理;
2.尽量在每一个函数或者较为复杂的方法面前加入注释,那样即使在编写代码的期间被突然打断事后也能快速找回思路,继续完成代码的编写。
3.发现代码使用过多的if else语句,可以加入新的方法减少程序的杂乱。

2.3 OOP-3: 电梯调度程序3

2.3.1 题目:

7-3 NCHU_单部电梯调度程序(类设计-迭代)
分数 50
较难
作者 段喜龙
单位 南昌航空大学
对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客类、队列类以及控制类,具体设计可参考如下类图。
类图.png
电梯运行规则与前阶段相同,但有如下变动情况:

乘客请求输入变动情况:外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)
注意:本次作业类设计必须符合如上要求(包含但不限于设计电梯类、乘客类、队列类以及控制类),凡是不符合类设计要求此题不得分,另外,PTA得分代码界定为第一次提交的最高分代码(因此千万不要把第一次及第二次电梯程序提交到本次题目中测试)。

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

电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<请求源楼层,请求目的楼层>,其中,请求源楼层表示乘客发起请求所在的楼层,请求目的楼层表示乘客想要到达的楼层。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:

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

1
20
<5,4>
<5>
<7>
end

输出样例1:
在这里给出相应的输出。例如:

Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
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
Current Floor: 5 Direction: DOWN
Open Door # Floor 5
Close Door
Current Floor: 4 Direction: DOWN
Open Door # Floor 4
Close Door

输入样例2:
在这里给出一组输入。例如:

1
20
<5,9>
<8>
<9,3>
<4>
<2>
end

输出样例2:
在这里给出相应的输出。例如:

Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
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
Current Floor: 8 Direction: UP
Open Door # Floor 8
Close Door
Current Floor: 9 Direction: UP
Open Door # Floor 9
Close Door
Current Floor: 8 Direction: DOWN
Current Floor: 7 Direction: DOWN
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Open Door # Floor 4
Close Door
Current Floor: 3 Direction: DOWN
Current Floor: 2 Direction: DOWN
Open Door # Floor 2
Close Door
Current Floor: 3 Direction: UP
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Current Floor: 8 Direction: UP
Current Floor: 9 Direction: UP
Open Door # Floor 9
Close Door
Current Floor: 8 Direction: DOWN
Current Floor: 7 Direction: DOWN
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door

代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
栈限制
8192 KB

2.3.2 源码

import java.util.*;

class Passenger {
    private int sourceFloor;
    private int destinationFloor;

    public Passenger(int sourceFloor, int destinationFloor) {
        this.sourceFloor = sourceFloor;
        this.destinationFloor = destinationFloor;
    }

    public int getSourceFloor() {
        return sourceFloor;
    }

    public int getDestinationFloor() {
        return destinationFloor;
    }

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

    @Override
    public int hashCode() {
        return Objects.hash(sourceFloor, destinationFloor);
    }
}

class Elevator {
    private int minFloor;
    private int maxFloor;
    private int currentFloor;
    private String currentDirection = "UP";

    public Elevator(int minFloor, int maxFloor) {
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.currentFloor = minFloor;
    }

    public int getCurrentFloor() {
        return currentFloor;
    }

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

    public String getCurrentDirection() {
        return currentDirection;
    }

    public void setCurrentDirection(String currentDirection) {
        this.currentDirection = currentDirection.toUpperCase();
    }

    public int getMinFloor() {
        return minFloor;
    }

    public void setMinFloor(int minFloor) {
        this.minFloor = minFloor;
    }

    public int getMaxFloor() {
        return maxFloor;
    }

    public void setMaxFloor(int maxFloor) {
        this.maxFloor = maxFloor;
    }
}

class RequestQueue {
    private ArrayList<Integer> internalRequests;
    private ArrayList<Passenger> externalRequests;
    private Integer lastInternalRequest = null;
    private Passenger lastExternalRequest = null;

    public RequestQueue() {
        this.internalRequests = new ArrayList<>();
        this.externalRequests = new ArrayList<>();
    }

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

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

    public ArrayList<Passenger> getExternalRequests() {
        return externalRequests;
    }

    public void setExternalRequests(ArrayList<Passenger> externalRequests) {
        this.externalRequests = externalRequests;
    }

    public void addInternalRequest(int floor, int minFloor, int maxFloor) {
        if (floor < minFloor || floor > maxFloor) {
            return;
        }
        if (lastInternalRequest != null && lastInternalRequest == floor) {
            return;
        }
        internalRequests.add(floor);
        lastInternalRequest = floor;
    }

    public void addExternalRequest(int sourceFloor, int destinationFloor, int minFloor, int maxFloor) {
        if (sourceFloor < minFloor || sourceFloor > maxFloor || destinationFloor < minFloor || destinationFloor > maxFloor) {
            return;
        }
        Passenger newRequest = new Passenger(sourceFloor, destinationFloor);
        if (lastExternalRequest != null && lastExternalRequest.equals(newRequest)) {
            return;
        }
        externalRequests.add(newRequest);
        lastExternalRequest = newRequest;
    }
}

class Controller {
    private Elevator elevator;
    private RequestQueue requestQueue;

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

    public Elevator getElevator() {
        return elevator;
    }

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

    public RequestQueue getQueue() {
        return requestQueue;
    }

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

    public void processRequests() {
        System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getCurrentDirection());
        while (!requestQueue.getInternalRequests().isEmpty() || !requestQueue.getExternalRequests().isEmpty()) {
            move();
        }
    }

    private void determineDirection() {
        if (!requestQueue.getInternalRequests().isEmpty() && !requestQueue.getExternalRequests().isEmpty()) {
            int infloor = requestQueue.getInternalRequests().get(0);
            int exfloor = requestQueue.getExternalRequests().get(0).getSourceFloor();

            if ("UP".equals(elevator.getCurrentDirection())) {
                if (infloor < elevator.getCurrentFloor() && exfloor < elevator.getCurrentFloor()) {
                    elevator.setCurrentDirection("DOWN");
                }
            }

            if ("DOWN".equals(elevator.getCurrentDirection())) {
                if (infloor > elevator.getCurrentFloor() && exfloor > elevator.getCurrentFloor()) {
                    elevator.setCurrentDirection("UP");
                }
            }
        } else if (!requestQueue.getInternalRequests().isEmpty()) {
            int infloor = requestQueue.getInternalRequests().get(0);
            if ("UP".equals(elevator.getCurrentDirection()) && infloor < elevator.getCurrentFloor()) {
                elevator.setCurrentDirection("DOWN");
            } else if ("DOWN".equals(elevator.getCurrentDirection()) && infloor > elevator.getCurrentFloor()) {
                elevator.setCurrentDirection("UP");
            }
        } else if (!requestQueue.getExternalRequests().isEmpty()) {
            int exfloor = requestQueue.getExternalRequests().get(0).getSourceFloor();
            if ("UP".equals(elevator.getCurrentDirection()) && exfloor < elevator.getCurrentFloor()) {
                elevator.setCurrentDirection("DOWN");
            } else if ("DOWN".equals(elevator.getCurrentDirection()) && exfloor > elevator.getCurrentFloor()) {
                elevator.setCurrentDirection("UP");
            }
        }
    }

    private void move() {
        if ("UP".equals(elevator.getCurrentDirection())) {
            elevator.setCurrentFloor(elevator.getCurrentFloor() + 1);
        } else if ("DOWN".equals(elevator.getCurrentDirection())) {
            elevator.setCurrentFloor(elevator.getCurrentFloor() - 1);
        }

        System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getCurrentDirection());

        if (shouldStop()) {
            System.out.println("Open Door # Floor " + elevator.getCurrentFloor());
            System.out.println("Close Door");
        }
        determineDirection();
    }

    private boolean shouldStop() {
        boolean inis = inStop();
        boolean exis = exStop();
        return inis || exis;
    }

    private boolean inStop() {
        if (!requestQueue.getInternalRequests().isEmpty() && requestQueue.getInternalRequests().get(0) == elevator.getCurrentFloor()) {
            requestQueue.getInternalRequests().remove(0);
            return true;
        }
        return false;
    }

    private boolean exStop() {
        if (!requestQueue.getExternalRequests().isEmpty()) {
            Passenger firstExternal = requestQueue.getExternalRequests().get(0);
            if (firstExternal.getSourceFloor() == elevator.getCurrentFloor()) {
                // 将目的楼层加入内部请求队列
                requestQueue.addInternalRequest(firstExternal.getDestinationFloor(), elevator.getMinFloor(), elevator.getMaxFloor());
                requestQueue.getExternalRequests().remove(0);
                return true;
            }
        }
        return false;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String data;
        int sourceFloor;
        int destinationFloor;
        int minFloor = Integer.parseInt(input.next());
        int maxFloor = Integer.parseInt(input.next());
        Elevator elevator = new Elevator(minFloor, maxFloor);
        RequestQueue requestQueue = new RequestQueue();
        Controller controller = new Controller(elevator, requestQueue);

        while (!(data = input.next()).equalsIgnoreCase("End")) {
            if (data.contains(",")) {
                if (!data.matches("<\\d+,\\s*\\d+>")) {
                    System.out.println("Wrong Format");
                    continue;
                }
                String[] parts = data.replaceAll("[<>]", "").split(",");
                sourceFloor = Integer.parseInt(parts[0].trim());
                destinationFloor = Integer.parseInt(parts[1].trim());
                requestQueue.addExternalRequest(sourceFloor, destinationFloor, minFloor, maxFloor);
            } else {
                if (!data.matches("<\\d+>")) {
                    System.out.println("Wrong Format");
                    continue;
                }
                sourceFloor = Integer.parseInt(data.replaceAll("[<>]", ""));
                requestQueue.addInternalRequest(sourceFloor, minFloor, maxFloor);
            }
        }

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

2.3.3 设计与分析

image

image

设计总结

  1. 类的设计
    Passenger 类:用于表示乘客请求,包含乘客的起始楼层和目标楼层,重写了equals和hashCode方法以确保对象比较的准确性。
    Elevator 类:代表电梯实体,记录电梯的当前楼层、最小楼层、最大楼层以及运行方向。
    RequestQueue 类:管理电梯的内外请求队列,提供添加内外请求的方法,同时避免重复请求。
    Controller 类:作为核心控制类,负责处理请求队列中的请求,决定电梯的运行方向,控制电梯移动,并判断电梯是否需要停靠。
    Main 类:程序入口,负责读取用户输入,将请求添加到请求队列,最后调用控制器处理请求。
  2. 数据结构设计
    使用ArrayList存储内部请求和外部请求,方便动态添加和删除请求。
  3. 交互设计
    程序通过命令行接收用户输入,支持内部请求(单个楼层)和外部请求(起始楼层和目标楼层),输入格式有严格要求,不符合格式会给出错误提示。

分析设计

  1. 优点
    模块化设计:各个类职责明确,提高了代码的可维护性和可扩展性。例如,若要添加新的电梯规则,可在Controller类中修改,而不影响其他类。
    错误处理:在输入验证和请求添加时进行了错误处理,如输入格式错误会给出提示,请求楼层超出范围则忽略该请求,增强了程序的健壮性。
    逻辑清晰:Controller类中的请求处理逻辑清晰,通过determineDirection方法确定电梯运行方向,move方法控制电梯移动,shouldStop方法判断是否停靠,使代码易于理解。
  2. 缺点
    性能问题:使用ArrayList存储请求,在删除请求时可能会导致性能下降,特别是请求队列较长时。可以考虑使用LinkedList来提高删除操作的性能。
    缺乏扩展性:当前设计仅支持单个电梯,若要扩展为多个电梯系统,需要对代码进行较大修改。可以引入电梯组的概念,将多个电梯作为一个整体管理。
    用户体验:程序仅通过命令行交互,缺乏图形界面,用户体验不佳。可以考虑添加图形界面,使用户更直观地看到电梯运行状态。
  3. 改进建议
    优化数据结构:将ArrayList替换为LinkedList,提高删除操作的性能。
    增强扩展性:设计电梯组类,将多个电梯作为一个整体管理,便于扩展为多电梯系统。
    改善用户体验:添加图形界面,使用户可以直观地看到电梯的运行状态和请求处理情况。

2.3.4 踩坑心得

1.实例中不需要添加太多方法,大部分核心方法都应在控制类中使用。
2.因为信息中部分的信息(如题目信息)可能会有多个,导致我们无法将提取的数据转到类中存储。因此需要用到以对应类为元素的动态数组来储存这些信息。调用信息时都提取那些动态数组中的内容,对动态数组中的数据进行加工

2.3.5 改进建议

循环条件结束条件要理清楚,注意是两个数组都为空时才结束,同时理清每一个方法的整体脉络,以避免判断句式出现问题

3.总结

作为 Java 初学者,这次电梯控制系统作业让我收获颇丰。在实践中,我首次将课堂所学的面向对象编程理论转化为实际代码,通过定义不同类并赋予各自清晰的职责,深刻理解了类与对象、封装、继承等核心概念,学会将现实场景抽象为程序模型。在处理电梯运行逻辑、请求队列管理等功能时,逐步掌握了复杂业务逻辑的拆解与实现方法,提升了分析问题和解决问题的能力。同时,在调试程序过程中,我学会了如何定位和修复代码错误,认识到合理注释与规范代码结构的重要性。虽然程序还存在性能、扩展性等不足,但这次经历让我对 Java 编程有了更直观的认识,也为后续学习指明了方向,激励我不断提升编程技能。

posted @ 2025-04-18 22:49    阅读(102)  评论(0)    收藏  举报