电梯调度分析

目录

正文

一、前言

这三次题目集总体而言难度还是比较高的,抛开三次题目集当中对于电梯调度的考察之外,第五次题目集主要考察了正则表达式的熟练使用,题量整体而言适中。第六次题目集主要考察了类的设计以及单一职责原则的使用,例如第二题汽车风挡玻璃雨刷问题当中定义了一个Agent类来进行雨刷速度的处理以及输出结果,题量整体较少。第七次题目集主要考察了类的设计以及单例设计模式,了解了蒙特卡洛仿真方法求圆周率的算法,题量整体较少。对于电梯调度的题目主要考察了LOOK算法,三次迭代层层递进,从单个类到设计不同的类不仅体现了单例模式的应用,也对电梯调度进行了优化。电梯调度的题目不仅对学生的逻辑思考能力做出要求,也考察了学生的耐心程度,因为在不断地试错以及调试的过程中虽然有痛苦,又迷茫,甚至想过放弃,但最终收获的喜悦是溢于言表的。

二、第一次电梯调度分析

1.题目介绍

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

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

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

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

2.设计与分析

  1. 首先,我们应该先分析需求:模拟单部电梯的运行过程。电梯的运行规则符合LOOK算法,所以我们先研究简易的LOOK算法的原理。
  2. LOOK算法的核心思想就是方向优先,即当前电梯运行方向的请求(电梯内请求和电梯外请求(此处要判断方向))没有执行完电梯不会转向。
  3. 分析完LOOK算法,本次电梯调度题目要求只需要设计一个类,属性有最大楼层数、最小楼层数、当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列7个属性。
  4. 其次,考虑如何存储电梯内部请求队列与外部楼层乘客请求队列,我当时考虑到了数组与链表,由于对数组较为熟悉,所以我选择数组来进行存储。
  5. 除了基本的构造方法以及Getter和Setter方法外,我设计了读取用户的输入的方法inputFloor()和运行电梯的方法runElevator(),读取用户的输入我是通过正则表达式从"<>"中提取出来楼层及方向并将其存储到外部请求队列中的数组当中并记录次数,内部请求队列同样如此。main方法可以通过这两个方法来实现模拟电梯的运行。我还设计了确定方向,改变方向的方法,以及开门的方法通过这些方法来进行LOOK算法的实现。

以下是第一次电梯调度的类图设计:

以下是第一次电梯调度的源代码:

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

class Elevator{
    private int minFloor;
    private int maxFloor;
    private int currentFloor;
    private int currentDirection;//0表示无方向,1表示向上,2表示向下
    private int runningState;

    //0表示外部请求,1表示内部请求
    private int[][] requestFloor = new int[2][100];
    //表示请求的次数,0表示外部请求次数,1表示内部请求次数
    private int[] requestNum = new int[100];
    //0表示外部请求,1表示内部请求
    private int[] requestDirection = new int[100];

    public Elevator(){

    }

    public Elevator(int minFloor, int maxFloor) {
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.currentFloor = 1;
        this.currentDirection = 0;
        this.runningState = 1;
    }

    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;
    }

    public int getCurrentFloor() {
        return currentFloor;
    }

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

    public int getCurrentDirection() {
        return currentDirection;
    }

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

    public int[][] getRequestFloor() {
        return requestFloor;
    }

    public void setRequestFloor(int[][] requestFloor) {
        this.requestFloor = requestFloor;
    }

    public int[] getRequestNum() {
        return requestNum;
    }

    public void setRequestNum(int[] requestNum) {
        this.requestNum = requestNum;
    }

    public int[] getRequestDirection() {
        return requestDirection;
    }

    public void setRequestDirection(int[] requestDirection) {
        this.requestDirection = requestDirection;
    }

    //确定方向
    public void setDirection(int i){
        if(i == 1){
            this.currentDirection = 1;
        }else if(i == 2){
            this.currentDirection = 2;
        }
    }

    //检查输入数据的合法性
    public boolean isValidFloor(int floor) {
        return floor >= minFloor && floor <= maxFloor;
    }

    //读取用户的输入
    public void inputFloor(){
        Scanner s = new Scanner(System.in);
        int minFloor = s.nextInt();
        int maxFloor = s.nextInt();
        while(true){
            String request = s.next();
            boolean hasComma1 = request.contains(",");
            boolean hasComma2 = request.contains("UP");
            boolean hasComma3 = request.contains("DOWN");
            String tmp = request.toUpperCase();
            if(tmp.equals("END")){
                break;
            }
            if(hasComma1){
                requestFloor[0][requestNum[0]] = intRegex(request);
                if(hasComma2){
                    requestDirection[requestNum[0]] = 1;
                }else if(hasComma3){
                    requestDirection[requestNum[0]] = 2;
                }
                requestNum[0]++;
            }else{
                requestFloor[1][requestNum[1]] = onlyIntRegex(request);
                requestNum[1]++;
            }
        }
        //System.out.println(requestNum[0]);
        //System.out.println(requestNum[1]);
    }

    public void improveDirection(int inner,int outter){
        if(outter == requestNum[0]){
            if(currentFloor > requestFloor[1][inner]){
                setDirection(2);
            }else if(currentFloor < requestFloor[1][inner]){
                setDirection(1);
            }
        }else if(inner == requestNum[1]){
            if(currentFloor > requestFloor[0][outter]){
                setDirection(2);
            }else if(currentFloor < requestFloor[0][outter]){
                setDirection(1);
            }
        }else if(currentFloor > requestFloor[0][outter] && currentFloor > requestFloor[1][inner]){
            setDirection(2);
        }else if(currentFloor < requestFloor[0][outter] && currentFloor < requestFloor[1][inner]){
            setDirection(1);
        }
    }

    public void openDoor(){
        System.out.println("Open Door # Floor " + this.currentFloor);
        System.out.println("Close Door");
    }

    public void runElevator(){
        int inner = 0;
        int outter = 0;
        setDirection(1);
        while(true){
            currentFloor = getCurrentFloor();
            currentDirection = getCurrentDirection();
            if(currentDirection == 1){
                System.out.println("Current Floor: " + this.currentFloor + " " + "Direction: UP");
            }
            if(currentDirection == 2){
                System.out.println("Current Floor: " + this.currentFloor + " " + "Direction: DOWN");
            }

            //当前楼层等于外部请求楼层并且并没有访问完所有的外部请求楼层
            if(currentFloor == requestFloor[0][outter] && outter != requestNum[0]){
                if(currentDirection == requestDirection[outter] || inner == requestNum[1]){
                    openDoor();
                    outter++;
                    improveDirection(inner,outter);
                }
            }

            //当前楼层等于内部请求楼层并且并没有访问完所有的内部请求楼层
            if(currentFloor == requestFloor[1][inner] && inner != requestNum[1]){
                if(currentFloor != requestFloor[0][outter]){
                    openDoor();
                    inner++;
                    improveDirection(inner,outter);
                }
            }
            
            //如果外部和内部的请求均完成的话则停止循环,楼梯停止
            if(inner == requestNum[1] && outter == requestNum[0]){
                this.runningState = 0;
                break;
            }

            if(getCurrentDirection() == 1){
                this.currentFloor++;
            }else if(getCurrentDirection() == 2){
                this.currentFloor--;
            }
        }
    }
    //外部的请求提取数字
    public int intRegex(String s) {
        String regex = "<(\\d+),([^>]*)>";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(s);

        if (matcher.find()) {
            return Integer.parseInt(matcher.group(1)); // 提取数字部分
        } else {
            return 0;
        }
    }

    //内部的请求提取数字
    public int onlyIntRegex(String s) {
        // 定义正则表达式,匹配以 < 开头,以 > 结尾,中间包含数字的字符串
        String regex = "<(\\d+)>";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(s);

        if (matcher.find()) {
            String numberStr = matcher.group(1);
            return Integer.parseInt(numberStr);
        } else {
            return 0;
        }
    }
}

public class Main{
    public static void main(String[] args) {
        Elevator elevator = new Elevator(1,100);

        //处理输入的楼层请求
        elevator.inputFloor();

        //运行电梯程序
        elevator.runElevator();
    }
}

以下是第一次电梯调度代码的分析

名称 数量
语句数 136
方法调用语句数 41
每个类方法数 11
最大嵌套深度 4
平均嵌套深度 1.46
平均复杂度 1.11
由于本次电梯只涉及一个类,嵌套深度比较高,代码的复杂性较高,这样会使代码的可读性降低,调试难度增加,可扩展性降低,所以在今后的学习中应该降低嵌套深度以增强代码的可读性。可以通过提取函数来降低嵌套深度,之后的改进中就是这样实现的。

3.踩坑心得

  1. 本次题目刚开始在理解LOOK算法的时候存在误解导致始终无法搞明白电梯到底是如何运行的,后来在与同学的讨论中成功理解了电梯的运行并通过程序实现。
  2. 没有搞清楚需求:end不区分大小写,导致程序出现运行超时的错误,后续通过toUpperCase方法将其全部转换为大写的END成功解决该问题。

  1. 没有仔细对输入输出格式进行比较,出现格式错误,所以在日后的学习中一定要看清楚题目。

4.改进建议

该程序中runElevator()方法并没有实现单一职责原则,因此对runElevator()方法对其功能进行细分拆解为开门,改变方向,输出,以及运行电梯四个方法,降低了嵌套深度。

以下是改进后的方法:

点击查看代码
    public void improveDirection(int inner,int outter){
        if(outter == requestNum[0]){
            if(currentFloor > requestFloor[1][inner]){
                setDirection(2);
            }else if(currentFloor < requestFloor[1][inner]){
                setDirection(1);
            }
        }else if(inner == requestNum[1]){
            if(currentFloor > requestFloor[0][outter]){
                setDirection(2);
            }else if(currentFloor < requestFloor[0][outter]){
                setDirection(1);
            }
        }else if(currentFloor > requestFloor[0][outter] && currentFloor > requestFloor[1][inner]){
            setDirection(2);
        }else if(currentFloor < requestFloor[0][outter] && currentFloor < requestFloor[1][inner]){
            setDirection(1);
        }
    }

    public void openDoor(){
        System.out.println("Open Door # Floor " + this.currentFloor);
        System.out.println("Close Door");
    }

    public void output(int currentDirection){
            if(currentDirection == 1){
                System.out.println("Current Floor: " + this.currentFloor + " " + "Direction: UP");
            }
            if(currentDirection == 2){
                System.out.println("Current Floor: " + this.currentFloor + " " + "Direction: DOWN");
            }
    }

    public void runElevator(){
        int inner = 0;
        int outter = 0;
        setDirection(1);
        while(true){
            currentFloor = getCurrentFloor();
            currentDirection = getCurrentDirection();

            //输出当前楼层的层数和方向
            output(currentDirection);
            
            //当前楼层等于外部请求楼层并且并没有访问完所有的外部请求楼层
            if(currentFloor == requestFloor[0][outter] && outter != requestNum[0]){
                if(currentDirection == requestDirection[outter] || inner == requestNum[1]){
                    openDoor();
                    outter++;
                    improveDirection(inner,outter);
                }
            }

            //当前楼层等于内部请求楼层并且并没有访问完所有的内部请求楼层
            if(currentFloor == requestFloor[1][inner] && inner != requestNum[1]){
                if(currentFloor != requestFloor[0][outter]){
                    openDoor();
                    inner++;
                    improveDirection(inner,outter);
                }
            }
            
            //如果外部和内部的请求均完成的话则停止循环,楼梯停止
            if(inner == requestNum[1] && outter == requestNum[0]){
                this.runningState = 0;
                break;
            }

            if(getCurrentDirection() == 1){
                this.currentFloor++;
            }else if(getCurrentDirection() == 2){
                this.currentFloor--;
            }
        }
    }

三、 第二次电梯调度分析

1.题目改进

对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客请求类、队列类以及控制类。

电梯运行规则与前阶段单类设计相同,但要处理如下情况:

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

2.设计与分析

  1. 第二次迭代由于第一次电梯使用的是数组,不太好处理这些需求,例如乘客请求不合理的出队等等,所以这一次我使用了链表来完成,大致逻辑没有改变,只不过是换了一种存储方式,所以还是比较容易实现的。
    2.需求是类设计符合单一职责原则,所以我设计了电梯类,请求队列类,外部请求类,控制类以及主类。
    3.整体没有变化,只不过是数组的添加移动改为入队出队。需求还要判断输入楼层是否合法以及乘客请求是否重复,这些都要在输入的时候进行判断,如果都满足的话就入队,否则不入队。

以下是第二次电梯调度的类图设计

以下是第二次电梯调度的源代码

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

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() {
    }

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

    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 void setMaxFloor(int maxFloor) {
        this.maxFloor = maxFloor;
    }

    public int getMinFloor() {
        return minFloor;
    }

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

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

//请求队列类
class RequestQueue {
    LinkedList<Integer> list1 = new LinkedList<>();
    LinkedList<ExternalRequest> list2 = new LinkedList<>();

    public RequestQueue() {
    }

    public RequestQueue(LinkedList<Integer> list1, LinkedList<ExternalRequest> list2) {
        this.list1 = list1;
        this.list2 = list2;
    }

    public LinkedList<Integer> getList1() {
        return list1;
    }

    public void setList1(LinkedList<Integer> list1) {
        this.list1 = list1;
    }

    public LinkedList<ExternalRequest> getList2() {
        return list2;
    }

    public void setList2(LinkedList<ExternalRequest> list2) {
        this.list2 = list2;
    }

    public void addInternalRequest(int floor) {
        if (list1.isEmpty() || list1.getLast().equals(floor) == false) {
            list1.add(floor);
        }
    }

    public void addExternalRequest(int floor, Direction direction) {
        ExternalRequest externalRequest = new ExternalRequest(floor, direction);
        //判断是否有重复的队列请求
        if (list2.isEmpty() || list2.getLast().isEquals(externalRequest) == false) {
            list2.add(externalRequest);
        }
    }
	public int getSize(){
		//System.out.println(list2.size());
        return list2.size();
    }
}

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

    public ExternalRequest() {
    }

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

    public Integer getFloor() {
        return floor;
    }

    public void setFloor(Integer floor) {
        this.floor = floor;
    }

    public Direction getDirection() {
        return direction;
    }

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

    public boolean isEquals(ExternalRequest request) {
        if(this == request){
            return true;
        }
        if(request == null){
            return false;
        }
        if(this.floor == request.floor && this.direction == request.direction){
            return true;
        }
        return false;
    }
}

//控制类
class Controller {
    private Elevator elevator = new Elevator();
    private RequestQueue queue = new RequestQueue();

    public Controller() {
    }

    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 setDirection(int i) {
        if (i == 1) {
            elevator.setDirection(Direction.UP);
        } else if (i == 2) {
            elevator.setDirection(Direction.DOWN);
        }
    }

    public void improveDirection() {
        int currentFloor = elevator.getCurrentFloor();
        if (!queue.getList1().isEmpty() && queue.getList2().isEmpty()) {
            int nextFloor = queue.getList1().get(0);
            if (currentFloor > nextFloor) {
                setDirection(2);
            } else if (currentFloor < nextFloor) {
                setDirection(1);
            }
        } else if (queue.getList1().isEmpty() && !queue.getList2().isEmpty()) {
            int nextFloor = queue.getList2().get(0).getFloor();
            if (currentFloor > nextFloor) {
                setDirection(2);
            } else if (currentFloor < nextFloor) {
                setDirection(1);
            }
        } else if (!queue.getList1().isEmpty() && !queue.getList2().isEmpty()) {
            int nextInnerFloor = queue.getList1().get(0);
            int nextOuterFloor = queue.getList2().get(0).getFloor();
            if (currentFloor > nextInnerFloor && currentFloor > nextOuterFloor) {
                setDirection(2);
            } else if (currentFloor < nextInnerFloor && currentFloor < nextOuterFloor) {
                setDirection(1);
            }
        }
    }


    //开门
    public void openDoor() {
        System.out.println("Open Door # Floor " + this.getElevator().getCurrentFloor());
        System.out.println("Close Door");
    }

    public void output() {
        if (elevator.getDirection() == Direction.UP) {
            System.out.println("Current Floor: " + elevator.getCurrentFloor() + " " + "Direction: UP");
        } else if (elevator.getDirection() == Direction.DOWN) {
            System.out.println("Current Floor: " + elevator.getCurrentFloor() + " " + "Direction: DOWN");
        }
    }

    //运行
    public void run() {
        setDirection(1);
        while (true) {
            int currentFloor = elevator.getCurrentFloor();
            Direction currentDirection = elevator.getDirection();

            // 输出当前楼层的层数和方向
            output();

            // 当前楼层等于外部请求楼层并且并没有访问完所有的外部请求楼层
            if (!queue.getList2().isEmpty()) {
                ExternalRequest externalRequest = queue.getList2().get(0);
                if(currentFloor == externalRequest.getFloor() && queue.getSize() == 1){
					openDoor();
                    queue.getList2().remove(0);
					//queue.getSize();
                    improveDirection();
					//System.out.println(queue.getList2().isEmpty());
				}
				else if (currentFloor == externalRequest.getFloor() && (currentDirection == externalRequest.getDirection() )) {
                    openDoor();
					//queue.getSize();
                    if(!queue.getList2().isEmpty()){
                        queue.getList2().remove(0);
                    }
                    //queue.getList2().remove(0);
                    improveDirection();
                }
            }

            // 当前楼层等于内部请求楼层并且并没有访问完所有的内部请求楼层
            if (!queue.getList1().isEmpty()) {
                int inner = queue.getList1().get(0);
                if (currentFloor == inner) {
                    openDoor();
                    queue.getList1().remove(0);
                    improveDirection();
                }
            }

            // 如果外部和内部的请求均完成的话则停止循环,楼梯停止
            if (queue.getList1().isEmpty() && queue.getList2().isEmpty()) {
                break;
            }

            if (elevator.getDirection() == Direction.UP) {
                currentFloor++;
                elevator.setCurrentFloor(currentFloor);
            } else if (elevator.getDirection() == Direction.DOWN) {
                currentFloor--;
                elevator.setCurrentFloor(currentFloor);
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int minFloor = s.nextInt();
        int maxFloor = s.nextInt();
        Elevator elevator = new Elevator(minFloor, maxFloor);
        RequestQueue requestQueue = new RequestQueue();
        Controller controller = new Controller(elevator, requestQueue);

        while (true) {
            String request = s.next();
            String tmp = request.toUpperCase();
            boolean hasComma1 = request.contains(",");
            boolean hasComma2 = request.contains("UP");
            boolean hasComma3 = request.contains("DOWN");
            if (tmp.equals("END")) {
                break;
            }
            if (hasComma1) {
                if (hasComma2) {
                    int floor = intRegex(request);
                    if (elevator.isValidFloor(floor)) {
                        requestQueue.addExternalRequest(floor, Direction.UP);
                    }
                } else if (hasComma3) {
                    int floor = intRegex(request);
                    if (elevator.isValidFloor(floor)) {
                        requestQueue.addExternalRequest(floor, Direction.DOWN);
                    }
                }
            } else {
                int floor = onlyIntRegex(request);
                if (elevator.isValidFloor(floor)) {
                    requestQueue.addInternalRequest(floor);
                }
            }
        }
        controller.run();
    }

    // 外部的请求提取数字
    public static int intRegex(String s) {
        String regex = "<(\\d+),([^>]*)>";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(s);

        if (matcher.find()) {
            return Integer.parseInt(matcher.group(1)); // 提取数字部分
        } else {
            return -1;
        }
    }

    // 内部的请求提取数字
    public static int onlyIntRegex(String s) {
        String regex = "<(\\d+)>";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(s);

        if (matcher.find()) {
            String numberStr = matcher.group(1);
            return Integer.parseInt(numberStr);
        } else {
            return -1;
        }
    }
}

以下是第二次电梯调度代码的分析

名称 数量
语句数 203
方法调用语句数 99
每个类方法数 13.33
最大嵌套深度 6
平均嵌套深度 1.42
平均复杂度 10.00
第二次迭代中方法调用语句数还是比较多的,这说明部分代码没有复用,降低了代码的可复用性。最大嵌套深度相较于第一次增加了,这是非常不好的,日后应该改进。

3.踩坑心得

  • 第二次迭代过程中遇到了运行超时的情况,这还是比较痛苦的,经过一系列的调试终于发现因为少考虑了如果当前楼层等于外部请求的最后一个请求楼层,此时无需考虑方向直接开门输出即可。

4.改进建议

本次迭代程序中圈复杂度也比较高,应该减少分支数量,使代码逻辑更为简单。

四、 第三次电梯调度分析

1.题目改进

对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客类、队列类以及控制类。

电梯运行规则与前阶段相同,但有如下变动情况:

  • 乘客请求输入变动情况:外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
  • 对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)

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

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

2.设计与分析

  1. 第三次迭代改变了外部请求的处理模式,由<楼层,方向>转变为<请求源楼层,请求目的楼层>,但由于对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列,所以逻辑与前两次基本一致。
  2. 取消了外部请求类转而替换为乘客类,乘客类中定义了请求源楼层,请求目的楼层两个属性,通过这两个属性即源楼层-目的楼层可以得出请求源楼层的方向,所以设计了getDirection方法。
  3. 通过正则表达式依然可以对输入进行处理,我是使用数组来临时存储外部请求的请求目的楼层,当内部请求全部入队之后,我将这个数组的请求目的楼层全部入队到内部队列的队尾。

以下是第三次电梯调度的类图设计

以下是第三次电梯调度的源代码

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

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() {
    }

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

    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 void setMaxFloor(int maxFloor) {
        this.maxFloor = maxFloor;
    }

    public int getMinFloor() {
        return minFloor;
    }

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

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

//请求队列类
class RequestQueue {
    LinkedList<Integer> list1 = new LinkedList<>();
    LinkedList<Passenger> list2 = new LinkedList<>();

    public RequestQueue() {
    }

    public RequestQueue(LinkedList<Integer> list1, LinkedList<Passenger> list2) {
        this.list1 = list1;
        this.list2 = list2;
    }

    public LinkedList<Integer> getList1() {
        return list1;
    }

    public void setList1(LinkedList<Integer> list1) {
        this.list1 = list1;
    }

    public LinkedList<Passenger> getList2() {
        return list2;
    }

    public void setList2(LinkedList<Passenger> list2) {
        this.list2 = list2;
    }

    public void addInternalRequest(int floor,int choose) {
        if (list1.isEmpty() || list1.getLast().equals(floor) == false) {
            if(choose == 0){
                list1.addLast(floor);
            }else if(choose == 1){
                list1.add(floor);
            }
        }
    }

    public void addExternalRequest(int floor, int targetFloor) {
        Passenger passenger = new Passenger(floor,targetFloor);
        //判断是否有重复的队列请求
        if (list2.isEmpty() || !(list2.getLast().isEquals(passenger)) ) {
            list2.add(passenger);
        }
    }
}


//乘客类
class Passenger{
    Integer sourceFloor = null;
    Integer destinationFloor = null;

    public Passenger() {
    }

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

    public Passenger(Integer destinationFloor) {
        this.destinationFloor = destinationFloor;
    }

    public Integer getSourceFloor() {
        return sourceFloor;
    }

    public void setSourceFloor(Integer sourceFloor) {
        this.sourceFloor = sourceFloor;
    }

    public Integer getDestinationFloor() {
        return destinationFloor;
    }

    public void setDestinationFloor(Integer destinationFloor) {
        this.destinationFloor = destinationFloor;
    }

    public Direction getDirection(){
        if(this.sourceFloor - this.destinationFloor < 0){
           
            return Direction.UP;
        }else if(this.sourceFloor - this.destinationFloor > 0){

            return Direction.DOWN;
        }else{
            return Direction.IDLE;
        }
    }

    public boolean isEquals(Passenger passenger) {
        if(this == passenger){
            return true;
        }
        if(passenger == null){
            return false;
        }
        if(this.sourceFloor == passenger.sourceFloor && this.destinationFloor == passenger.destinationFloor ){
            return true;
        }
        return false;
    }
}

//控制类
class Controller {
    private Elevator elevator = new Elevator();
    private RequestQueue queue = new RequestQueue();

    public Controller() {
    }

    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 setDirection(int i) {
        if (i == 1) {
            elevator.setDirection(Direction.UP);
        } else if (i == 2) {
            elevator.setDirection(Direction.DOWN);
        }
    }

    public void improveDirection() {
        int currentFloor = elevator.getCurrentFloor();
        if (!queue.getList1().isEmpty() && queue.getList2().isEmpty()) {
            int nextFloor = queue.getList1().get(0);
            if (currentFloor > nextFloor) {
                setDirection(2);
            } else if (currentFloor < nextFloor) {
                setDirection(1);
            }
        } else if (queue.getList1().isEmpty() && !queue.getList2().isEmpty()) {
            int nextFloor = queue.getList2().get(0).getSourceFloor();
            if (currentFloor > nextFloor) {
                setDirection(2);
            } else if (currentFloor < nextFloor) {
                setDirection(1);
            }
        } else if (!queue.getList1().isEmpty() && !queue.getList2().isEmpty()) {
            int nextInnerFloor = queue.getList1().get(0);
            int nextOuterFloor = queue.getList2().get(0).getSourceFloor();
            if (currentFloor > nextInnerFloor && currentFloor > nextOuterFloor) {
                setDirection(2);
            } else if (currentFloor < nextInnerFloor && currentFloor < nextOuterFloor) {
                setDirection(1);
            }
        }
    }


    //开门
    public void openDoor() {
        System.out.println("Open Door # Floor " + this.getElevator().getCurrentFloor());
        System.out.println("Close Door");
    }

    public void output() {
        if (elevator.getDirection() == Direction.UP) {
            System.out.println("Current Floor: " + elevator.getCurrentFloor() + " " + "Direction: UP");
        } else if (elevator.getDirection() == Direction.DOWN) {
            System.out.println("Current Floor: " + elevator.getCurrentFloor() + " " + "Direction: DOWN");
        }
    }

    //运行
    public void run() {
        setDirection(1);
        while (true) {
            int currentFloor = elevator.getCurrentFloor();
            Direction currentDirection = elevator.getDirection();

            // 输出当前楼层的层数和方向
            output();
           
            // 当前楼层等于外部请求楼层并且并没有访问完所有的外部请求楼层
            if (!queue.getList2().isEmpty()) {
                Passenger passenger = queue.getList2().get(0);
                    if(currentFloor == passenger.getSourceFloor()){
                        if(currentDirection == passenger.getDirection() || queue.getList1().isEmpty()){
                            openDoor();

                            if(!queue.getList2().isEmpty()){
                                queue.getList2().remove(0);
                                //System.out.println(queue.getList2().size());
                            }
                            improveDirection();
                        }else if(currentDirection != passenger.getDirection()){
                            int inner = queue.getList1().get(0);
                            if(currentDirection == Direction.UP && currentFloor > inner){
                                openDoor();
                                if(!queue.getList2().isEmpty()){
                                    queue.getList2().remove(0);
                                    //System.out.println(queue.getList2().size());
                                }
                                setDirection(2);
                            }else if(currentDirection == Direction.DOWN && currentFloor < inner){
                                openDoor();
                                if(!queue.getList2().isEmpty()){
                                    queue.getList2().remove(0);
                                    //System.out.println(queue.getList2().size());
                                }
                                setDirection(1);
                            }
                        }
                    }
                }
            

            // 当前楼层等于内部请求楼层并且并没有访问完所有的内部请求楼层
            if (!queue.getList1().isEmpty()) {
                int inner = queue.getList1().get(0);
                if (currentFloor == inner) {
                    openDoor();
                    queue.getList1().remove(0);
                    improveDirection();
                }
            }

            // 如果外部和内部的请求均完成的话则停止循环,楼梯停止
            if (queue.getList1().isEmpty() && queue.getList2().isEmpty()) {
                break;
            }

            if (elevator.getDirection() == Direction.UP) {
                currentFloor++;
                elevator.setCurrentFloor(currentFloor);
            } else if (elevator.getDirection() == Direction.DOWN) {
                currentFloor--;
                elevator.setCurrentFloor(currentFloor);
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int minFloor = s.nextInt();
        int maxFloor = s.nextInt();
        Elevator elevator = new Elevator(minFloor, maxFloor);
        RequestQueue requestQueue = new RequestQueue();
        Controller controller = new Controller(elevator, requestQueue);
        Passenger passenger = new Passenger();
        int count = 0;
        int[] target = new int[100];
        while (true) {
            String request = s.next();
            String tmp = request.toUpperCase();
            boolean hasComma = request.contains(",");
            if (tmp.equals("END")) {
                break;
            }
            if(hasComma){
                int[] result = intRegex(request);
                if(elevator.isValidFloor(result[0]) && elevator.isValidFloor(result[1])){
                    requestQueue.addExternalRequest(result[0],result[1]);
                    //System.out.println(requestQueue.getList2().size());
                    target[count] = result[1];
                    count++;
                }
            }else{
                int floor = onlyIntRegex(request);
                if (elevator.isValidFloor(floor)) {
                    requestQueue.addInternalRequest(floor,1);
                }
            }
        }
        for(int i = 0;i < count;i++){
            requestQueue.addInternalRequest(target[i],0);
        }
        controller.run();
    }

    // 外部的请求提取数字
    public static int[] intRegex(String s) {
        String regex = "<(\\d+),(\\d+)>";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(s);

        if (matcher.find()) {
            int num1 = Integer.parseInt(matcher.group(1));
            int num2 = Integer.parseInt(matcher.group(2));
            return new int[]{num1, num2};
        } else {
            return new int[]{-1, -1};
        }
    }


    // 内部的请求提取数字
    public static int onlyIntRegex(String s) {
        String regex = "<(\\d+)>";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(s);

        if (matcher.find()) {
            String numberStr = matcher.group(1);
            return Integer.parseInt(numberStr);
        } else {
            return -1;
        }
    }
}

以下是第三次电梯调度的分析

名称 数量
语句数 224
方法调用语句数 107
每个类方法数 13.67
最大嵌套深度 7
平均嵌套深度 1.60
平均复杂度 9.00
main方法的嵌套深度最深,这是因为其使用了while循环,也使用了for循环。圈复杂度也比较高,同时方法调用语句数也比较多。

3.踩坑心得

本次电梯题目还是遇到了许多不同的情况,现在想来也是别有一番趣味。

  1. 第一次仍然犯了之前没有看清需求的错误,没有注意到要将外部请求队列中的请求目的楼层加入到请求内部队列(加到队尾),写完之后用题目中的测试用例测试之后发现不对之后仔细分析了需求才解决。
  2. 第二次是因为少考虑了外部请求队列当中的请求源楼层还有方向,所以只通过了后三个测试点。

  1. 第三次考虑方向之后又遇见了一个棘手的问题:运行超时,通过调试以及使用javac,java来运行程序时输出的结果并询问同学发现是因为之前的电梯程序中改变方向时少考虑了两种情况。当电梯运行到外部请求的源楼层时如果方向与源楼层方向相反,即当当前方向向上,如果下一个内部请求低于当前楼层,改变方向向下;当当前方向向下,如果下一个内部请求高于当前楼层,改变方向向上。

4.改进建议

综合以上三次迭代:

  1. 代码注释较少,这样会降低代码的可读性以及可维护性,不利于后续的开发。在今后的学习中,我要尝试在复杂逻辑处添加注释在关键函数和类上添加注释
  2. 方法调用语句数较多,分支数量较多,这样导致了嵌套深度的增加以及圈复杂度的增加,日后应该提取方法和降低分支数量,以图嵌套深度和圈复杂度的降低,增强代码的可读性。

五、 总结

  1. 这三次电梯的迭代从单个电梯类到类的设计,体现了单一职责原则,使得每个类或模块的职责明确,代码的结构更加清晰。当其他开发者阅读代码时,能够快速理解每个类或模块的功能。
  2. 这三次电梯迭代不仅强化了正则表达式的使用,对它的使用变得更为娴熟,而且提高了我们的逻辑思维能力抽象的能力,这对我们的帮助是十分大的。
  3. 我们还练习了减少耦合,从单个类到后面的多个类,我们设计出了控制类Control,通过减少耦合提高代码的可维护性,可读性,可复用性。
  4. 这三次迭代同样是对我们的一种锻炼,虽然道路比较艰辛,但收获满满。从刚开始的手忙脚乱到得心应手,细心,耐心,只有投入足够的时间才会收获不一样的喜悦。
  5. 当然在今后的学习中我们要强化单一职责原则的使用以及降低代码的嵌套深度和圈复杂度,提高代码的可读性,向着高质量代码前进!
posted @ 2025-04-20 10:27  briza  阅读(50)  评论(0)    收藏  举报