第一次blog作业

一、前言
Java这门语言的学习已经开始接近半个学期,一开始,我以为Java只是一门和c语言类似的语言,有相同的基础语法和许多名称相同的库,甚至在前三次pta作业完成之后我都认为Java和c语言区别不大,但在完成了第四次作业中各个类的设计和私有属性设计的任务后,我才慢慢感觉到了这门课的不同,而后的三次作业,也就是第五次到第七次作业,就像是推开了一扇新的大门,我才真正认识到我们这门课的与众不同。
在pta第5次到第7次中,使用了很多新知识,例如正则表达式,类的设计,Linklist,Arraylist等,题目量其实并不大,但是题目难度大,每一次作业都要花费五六天的时间去调试和修改,特别是每一次作业的最后一题,需要花很多时间去打磨,算法逻辑设计,类设计,处理类与类之间的关系等都需要花费我很多的时间。我认为,难度最大的就是第一次作业中电梯题目的算法设计,只要将算法设计好了,后面两次题目就只是在第一次作业上进行合理的类设计和功能的迭代,难度也大大降低了。
二、设计与分析
第五次作业:

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

class Elevator {
    private int minFloor;
    private int maxFloor;
    private int currentFloor;
    private String direction;
    private String state;
    private ArrayList<Integer> internalRequests;
    private ArrayList<Integer> externalRequests;
    private ArrayList<String> externalDirections;

    public Elevator(int minFloor, int maxFloor) {
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.currentFloor = minFloor;
        this.direction = "IDLE";
        this.state = "Stopped";
        this.internalRequests = new ArrayList<>();
        this.externalRequests = new ArrayList<>();
        this.externalDirections = new ArrayList<>();
    }

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

    public void processInput(String input) {
        String internalPattern = "<(\\d+)>";
        String externalPattern = "<(\\d+),(UP|DOWN)>";

        Pattern intPat = Pattern.compile(internalPattern);
        Pattern extPat = Pattern.compile(externalPattern);

        Matcher intMatcher = intPat.matcher(input);
        while (intMatcher.find()) {
            int floor = Integer.parseInt(intMatcher.group(1));
            if (isValidFloor(floor)) {
                internalRequests.add(floor);
            }
        }

        Matcher extMatcher = extPat.matcher(input);
        while (extMatcher.find()) {
            int floor = Integer.parseInt(extMatcher.group(1));
            String direction = extMatcher.group(2);
            if (isValidFloor(floor)) {
                externalRequests.add(floor);
                externalDirections.add(direction);
            }
        }
    }

    public void run() {
        while (!internalRequests.isEmpty() || !externalRequests.isEmpty()) {
            if (direction.equals("IDLE")) {
                findInitialDirection();
            }
            int targetFloor = getNextTargetFloor();
            moveToFloor(targetFloor);
        }
    }

    private void findInitialDirection() {
        int nearestTarget = Integer.MAX_VALUE;
        if (!externalRequests.isEmpty()) {
            nearestTarget = externalRequests.get(0);
        } else if (!internalRequests.isEmpty()) {
            nearestTarget = internalRequests.get(0);
        }
        if (nearestTarget > currentFloor) {
            direction = "UP";
        } else if (nearestTarget < currentFloor) {
            direction = "DOWN";
        }
        System.out.println("Current Floor: " + currentFloor + " Direction: " + direction);
    }

    private int getNextTargetFloor() {
        int internalTarget = -1;
        int externalTarget = -1;
        String externalDir = "";

        if (!internalRequests.isEmpty()) {
            internalTarget = internalRequests.get(0);
        }
        if (!externalRequests.isEmpty()) {
            externalTarget = externalRequests.get(0);
            externalDir = externalDirections.get(0);
        }

        if (internalTarget != -1 && externalTarget != -1) {
            boolean isInternalSameDirection = isSameDirection(internalTarget);
            boolean isExternalSameDirection = isSameDirection(externalTarget);
            if (isInternalSameDirection &&!isExternalSameDirection)
            {
                return internalRequests.remove(0);
            }
            else if (!isInternalSameDirection && isExternalSameDirection)
            {
                this.direction = externalDir;
                externalDirections.remove(0);
                isExternalSameDirection = externalDir.equals(direction);
                if(!isExternalSameDirection)
                    {
                        changeDirection();
                    }
                return externalRequests.remove(0);
            }
            else if (isInternalSameDirection && isExternalSameDirection)
            {
                isExternalSameDirection = externalDir.equals(direction);
                if (isInternalSameDirection && isExternalSameDirection)
                {
                    if (Math.abs(internalTarget - currentFloor) <= Math.abs(externalTarget - currentFloor)) {
                    return internalRequests.remove(0);
                } else {
                    externalDirections.remove(0);
                    return externalRequests.remove(0);
                }
                }
                else 
                {
                    return internalRequests.remove(0);
                }
            }
            else 
            {
                isExternalSameDirection = externalDir.equals(direction);
                if (!isInternalSameDirection && !isExternalSameDirection)
                {
                    if (Math.abs(internalTarget - currentFloor) <= Math.abs(externalTarget - currentFloor)) {
                        changeDirection();
                    return internalRequests.remove(0);
                } else {
                        changeDirection();
                    externalDirections.remove(0);
                    return externalRequests.remove(0);
                }
                }
                else 
                {
                    changeDirection();
                    return internalRequests.remove(0);
                }
            }
        } else if (internalTarget != -1) {
            return internalRequests.remove(0);
        } else if (externalTarget != -1) {
            direction = externalDirections.remove(0);
            return externalRequests.remove(0);
        }
        changeDirection();
        return getNextTargetFloor();
    }

    private boolean isSameDirection(int targetFloor) {
        if (direction.equals("UP")) {
            return targetFloor > currentFloor;
        } else {
            return targetFloor < currentFloor;
        }
    }

    private void changeDirection() {
        if (direction.equals("UP")) {
            direction = "DOWN";
        } else {
            direction = "UP";
        }
    }

    private void moveToFloor(int targetFloor) {
        while (currentFloor != targetFloor) {
            if (targetFloor > currentFloor) {
                currentFloor++;
                direction = "UP";
            } else {
                currentFloor--;
                direction = "DOWN";
            }
            System.out.println("Current Floor: " + currentFloor + " Direction: " + direction);
        }
        System.out.println("Open Door # Floor " + currentFloor);
        System.out.println("Close Door");
        removeProcessedRequests(currentFloor);
    }

    private void removeProcessedRequests(int floor) {
        if (!internalRequests.isEmpty() && internalRequests.get(0) == floor) {
            internalRequests.remove(0);
        }
        if (!externalRequests.isEmpty() && externalRequests.get(0) == floor) {
            externalRequests.remove(0);
            externalDirections.remove(0);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int minFloor = scanner.nextInt();
        int maxFloor = scanner.nextInt();
        scanner.nextLine();

        StringBuilder input = new StringBuilder();
        String line;
        while (true) {
            line = scanner.nextLine();
            if (line.trim().equalsIgnoreCase("end")) {
                break;
            }
            input.append(line).append(" ");
        }
        Elevator elevator = new Elevator(minFloor, maxFloor);
        elevator.processInput(input.toString());
        elevator.run();
        scanner.close();
    }
}    
在我的代码中,先是创建一个电梯类,并且在电梯类中定义最大最小楼层以及当前楼层,再创建两个动态数组去存储内外部的队列请求,再用Elevator.getNextTargetFloor()方法来比较内外部请求,从而选择出下一步要到达的楼层,最后在内外部请求队列都为空时,电梯停止运行,并且将所有的运行轨迹输出。

类图:

复杂度图:

其中sourcemonitor图中解释如下:
行数:224 行
语句数:153 条
分支语句百分比:25.5% ,意味着约四分之一语句是分支逻辑(if-else)
方法调用语句数:84 条
注释行百分比:0.0% ,即代码无注释
类和接口数量:2 个
每个类的平均方法数:5.50 个
每个方法的平均语句数):11.64 条
最复杂方法的行号:82 行,方法为 Elevator.getNextTargetFloor()
最大复杂度为25,方法也是Elevator.getNextTargetFloor()
最深代码块行号:119 行
最大代码块深度为 6
平均代码块深度:2.67
平均复杂度:5.45

分析类图,第一次作业只分为两个类,在我的代码中,各个类职责不够单一,例如:Elevator类承担了诸如输入处理(processInput )、运行逻辑(run )、方向判断(findInitialDirection )等多种职责。导致类的功能过于复杂,违反了单一职责原则,并且代码的复用性和可维护性低,不利于后期对代码的维护和扩展,这一点在后面两次作业我深有体会,正因为前期类的设计不够合理,才会导致我第二次作业花费了大量的时间去重新设计类和处理类之间的关系。
分析复杂度图,发现我的代码主要有四个方面的问题,第一是我的代码大量使用了if-else逻辑嵌套结构,导致我的代码复杂度极高,可读性低,会严重影响后期对代码的调试,修改维护和扩展,同时也导致我第二次作业花费了大量时间去调整类与类的关系。第二就是我的代码严重缺乏注释,注释行百分比为零,这同样会导致代码的可读性降低。第三是有部分方法的复杂度较高,如 Elevator.getNextTargetFloor() ,在Java学习过程中,老师曾经说过:高复杂度可能导致代码难以理解、调试和修改,违背代码简洁易维护原则。而我的代码中的 Elevator.getNextTargetFloor() 方法复杂度高达25,导致我的代码变得臃肿,难以理解。第四个方面则是代码深度不均衡,处于1 - 3时语句数较多,可能存在嵌套过深问题,也会影响代码可读性与可维护性。

第六次作业:

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

// 乘客请求类
class PassengerRequest {
    private int floor;
    private String direction;

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

    public int getFloor() {
        return floor;
    }

    public String getDirection() {
        return direction;
    }

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

    public int hashCode() {
        int result = floor;
        result = 31 * result + (direction != null? direction.hashCode() : 0);
        return result;
    }

    public static PassengerRequest parse(String input) {
        String internalPattern = "<(\\d+)>";
        String externalPattern = "<(\\d+),(UP|DOWN)>";

        Pattern intPat = Pattern.compile(internalPattern);
        Pattern extPat = Pattern.compile(externalPattern);

        Matcher intMatcher = intPat.matcher(input);
        if (intMatcher.find()) {
            int floor = Integer.parseInt(intMatcher.group(1));
            return new PassengerRequest(floor, null);
        }

        Matcher extMatcher = extPat.matcher(input);
        if (extMatcher.find()) {
            int floor = Integer.parseInt(extMatcher.group(1));
            String direction = extMatcher.group(2);
            return new PassengerRequest(floor, direction);
        }
        return null;
    }
}

// 电梯类
class Elevator {
    private int minFloor;
    private int maxFloor;
    private int currentFloor;
    private String direction;
    private String state;

    public Elevator(int minFloor, int maxFloor) {
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.currentFloor = minFloor;
        this.direction = "IDLE";
        this.state = "Stopped";
    }

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

    public int getCurrentFloor() {
        return currentFloor;
    }

    public String getDirection() {
        return direction;
    }

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

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

// 请求队列类
class RequestQueue {
    private List<PassengerRequest> requests;
    private Elevator elevator;

    public RequestQueue(Elevator elevator) {
        this.requests = new ArrayList<>();
        this.elevator = elevator;
    }

    public void addRequest(PassengerRequest request) {
        if (elevator.isValidFloor(request.getFloor())) {
            if (requests.isEmpty() || !requests.get(requests.size() - 1).equals(request)) {
                requests.add(request);
            }
        }
    }

    public List<PassengerRequest> getRequests() {
        return requests;
    }

    public void removeRequest(PassengerRequest request) {
        requests.remove(request);
    }
}

// 电梯控制类
class ElevatorController {
    private Elevator elevator;
    private RequestQueue requestQueue;
    private ArrayList<Integer> internalRequests;
    private ArrayList<Integer> externalRequests;
    private ArrayList<String> externalDirections;

    public ElevatorController(Elevator elevator, RequestQueue requestQueue) {
        this.elevator = elevator;
        this.requestQueue = requestQueue;
        this.internalRequests = new ArrayList<>();
        this.externalRequests = new ArrayList<>();
        this.externalDirections = new ArrayList<>();
    }

    public void processInput(String input) {
        String internalPattern = "<(\\d+)>";
        String externalPattern = "<(\\d+),(UP|DOWN)>";

        Pattern intPat = Pattern.compile(internalPattern);
        Pattern extPat = Pattern.compile(externalPattern);

        Matcher intMatcher = intPat.matcher(input);
        Integer lastInternalFloor = null;
        while (intMatcher.find()) {
            int floor = Integer.parseInt(intMatcher.group(1));
            if (elevator.isValidFloor(floor) && (lastInternalFloor == null || lastInternalFloor != floor)) {
                internalRequests.add(floor);
                lastInternalFloor = floor;
            }
        }

        Matcher extMatcher = extPat.matcher(input);
        PassengerRequest lastExternalRequest = null;
        while (extMatcher.find()) {
            int floor = Integer.parseInt(extMatcher.group(1));
            String direction = extMatcher.group(2);
            PassengerRequest newRequest = new PassengerRequest(floor, direction);
            if (lastExternalRequest == null || !lastExternalRequest.equals(newRequest)) {
                requestQueue.addRequest(newRequest);
                lastExternalRequest = newRequest;
            }
        }

        for (PassengerRequest request : requestQueue.getRequests()) {
            if (request.getDirection() != null) {
                externalRequests.add(request.getFloor());
                externalDirections.add(request.getDirection());
            }
        }
    }

    public void run() {
        while (!internalRequests.isEmpty() || !externalRequests.isEmpty()) {
            if (elevator.getDirection().equals("IDLE")) {
                findInitialDirection();
            }
            int targetFloor = getNextTargetFloor();
            moveToFloor(targetFloor);
        }
    }

    private void findInitialDirection() {
        int nearestTarget = Integer.MAX_VALUE;
        if (!externalRequests.isEmpty()) {
            nearestTarget = externalRequests.get(0);
        } else if (!internalRequests.isEmpty()) {
            nearestTarget = internalRequests.get(0);
        }
        if (nearestTarget > elevator.getCurrentFloor()) {
            elevator.setDirection("UP");
        } else if (nearestTarget < elevator.getCurrentFloor()) {
            elevator.setDirection("DOWN");
        }
        System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getDirection());
    }

    private int getNextTargetFloor() {
        int internalTarget = -1;
        int externalTarget = -1;
        String externalDir = "";

        if (!internalRequests.isEmpty()) {
            internalTarget = internalRequests.get(0);
        }
        if (!externalRequests.isEmpty()) {
            externalTarget = externalRequests.get(0);
            externalDir = externalDirections.get(0);
        }

        if (internalTarget != -1 && externalTarget != -1) {
            boolean isInternalSameDirection = isSameDirection(internalTarget);
            boolean isExternalSameDirection = isSameDirection(externalTarget);
            if (isInternalSameDirection && !isExternalSameDirection) {
                return internalRequests.remove(0);
            } else if (!isInternalSameDirection && isExternalSameDirection) {
                elevator.setDirection(externalDir);
                externalDirections.remove(0);
                isExternalSameDirection = externalDir.equals(elevator.getDirection());
                if (!isExternalSameDirection) {
                    changeDirection();
                }
                return externalRequests.remove(0);
            } else if (isInternalSameDirection && isExternalSameDirection) {
                isExternalSameDirection = externalDir.equals(elevator.getDirection());
                if (isInternalSameDirection && isExternalSameDirection) {
                    if (Math.abs(internalTarget - elevator.getCurrentFloor()) <= Math.abs(externalTarget - elevator.getCurrentFloor())) {
                        return internalRequests.remove(0);
                    } else {
                        externalDirections.remove(0);
                        return externalRequests.remove(0);
                    }
                } else {
                    return internalRequests.remove(0);
                }
            } else {
                isExternalSameDirection = externalDir.equals(elevator.getDirection());
                if (!isInternalSameDirection && !isExternalSameDirection) {
                    if (Math.abs(internalTarget - elevator.getCurrentFloor()) <= Math.abs(externalTarget - elevator.getCurrentFloor())) {
                        changeDirection();
                        return internalRequests.remove(0);
                    } else {
                        changeDirection();
                        externalDirections.remove(0);
                        return externalRequests.remove(0);
                    }
                } else {
                    changeDirection();
                    return internalRequests.remove(0);
                }
            }
        } else if (internalTarget != -1) {
            return internalRequests.remove(0);
        } else if (externalTarget != -1) {
            elevator.setDirection(externalDirections.remove(0));
            return externalRequests.remove(0);
        }
        changeDirection();
        return getNextTargetFloor();
    }

    private boolean isSameDirection(int targetFloor) {
        if (elevator.getDirection().equals("UP")) {
            return targetFloor > elevator.getCurrentFloor();
        } else {
            return targetFloor < elevator.getCurrentFloor();
        }
    }

    private void changeDirection() {
        if (elevator.getDirection().equals("UP")) {
            elevator.setDirection("DOWN");
        } else {
            elevator.setDirection("UP");
        }
    }

    private void moveToFloor(int targetFloor) {
        while (elevator.getCurrentFloor() != targetFloor) {
            if (targetFloor > elevator.getCurrentFloor()) {
                elevator.setCurrentFloor(elevator.getCurrentFloor() + 1);
                elevator.setDirection("UP");
            } else {
                elevator.setCurrentFloor(elevator.getCurrentFloor() - 1);
                elevator.setDirection("DOWN");
            }
            System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getDirection());
        }
        System.out.println("Open Door # Floor " + elevator.getCurrentFloor());
        System.out.println("Close Door");
        removeProcessedRequests(targetFloor);
    }

    private void removeProcessedRequests(int floor) {
        if (!internalRequests.isEmpty() && internalRequests.get(0) == floor) {
            internalRequests.remove(0);
        }
        if (!externalRequests.isEmpty() && externalRequests.get(0) == floor) {
            externalRequests.remove(0);
            externalDirections.remove(0);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int minFloor = scanner.nextInt();
        int maxFloor = scanner.nextInt();
        scanner.nextLine();

        StringBuilder input = new StringBuilder();
        String line;
        while (true) {
            line = scanner.nextLine();
            if (line.trim().equalsIgnoreCase("end")) {
                break;
            }
            input.append(line).append(" ");
        }

        Elevator elevator = new Elevator(minFloor, maxFloor);
        RequestQueue requestQueue = new RequestQueue(elevator);
        ElevatorController controller = new ElevatorController(elevator, requestQueue);
        controller.processInput(input.toString());
        controller.run();

        scanner.close();
    }
}    

在这次作业中,要求对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则,将电梯类分为多个类,例如乘客请求类,电梯控制类等,乘客请求类负责处理乘客请求,利用正则表达式来区分内外部请求,并将内外部请求分为两个动态数组,再用电梯控制类来获取下一步要前往的目标楼层,从而控制电梯的运行,还添加了查重方法ElevatorController.removeProcessedRequests();将使用者内外部多个连续的请求删除,只保留第一个请求,例如<3><3><3>过滤为<3>,同样是在内外部请求度队列都为空时停止运行,再将所有的运行轨迹输出。
类图:

复杂度图:

其中sourcemonitor图中解释如下:
行数:337 行
语句数:228 条
分支语句百分比:19.7% ,意味着约五分之一语句是分支逻辑(if - else)
方法调用语句数:137 条
注释行百分比:1.5% ,即代码中注释占比较少
类和接口数量:1 个
每个类的平均方法数:26.00 个
每个方法的平均语句数:8.46 条
最复杂方法的行号:309 行,方法为 Main.main ()
最大复杂度为 3,方法为 Main.main ()
最深代码块行号:231 行
最大代码块深度为 5
平均代码块深度:1.54
平均复杂度:3.00

第二次作业要求我们将第一次作业中的电梯类分为电梯类、乘客请求类、队列类以及控制类,并且合理设计各个类之间的关系,难度相较于前一次作业更低,但是也要花费很多的时间,前一次作业主要花费时间在于算法的设计,每次比较内外部请求队列的队头并且选择合适的请求出队,而第二次作业主要花费时间在于如何去设计类,如何去处理类之间的关系才能使代码的可读性和可维护性更高,如果说第一次作业是前期的代码算法设计工作,那第二次作业就是代码的完善和优化工作,这两次工作缺一不可。
观察第一张类图和第二张复杂度图,可以发现平均代码深度从2.67变成了1.54,平均复杂度也从5.45变成了3.00,这说明代码的可读性和可维护性大大提升,并且代码中if-else逻辑嵌套结构也从四分之一变成了五分之一,最复杂方法的复杂度从25变成了3,说明代码在第一次作业时出现复杂度高,分支逻辑嵌套结构占比大,代码深度不均衡等各个问题都得了改善,虽然说并没有完全解决这些问题,但相较于前一次作业,算得上是脱胎换骨了。

第七次作业:

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

// 乘客类
class Passenger {
    private int floor;
    private String direction;

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

    public int getFloor() {
        return floor;
    }

    public String getDirection() {
        return direction;
    }

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

    public int hashCode() {
        int result = floor;
        result = 31 * result + (direction != null? direction.hashCode() : 0);
        return result;
    }

    public static Passenger parse(String input) {
        String internalPattern = "<(\\d+)>";
        String externalPattern = "<(\\d+),(\\d+)>";

        Pattern intPat = Pattern.compile(internalPattern);
        Pattern extPat = Pattern.compile(externalPattern);

        Matcher intMatcher = intPat.matcher(input);
        if (intMatcher.find()) {
            int floor = Integer.parseInt(intMatcher.group(1));
            return new Passenger(floor, null);
        }

        Matcher extMatcher = extPat.matcher(input);
        if (extMatcher.find()) {
            int fromFloor = Integer.parseInt(extMatcher.group(1));
            int toFloor = Integer.parseInt(extMatcher.group(2));
            String direction = toFloor > fromFloor? "UP" : "DOWN";
            return new Passenger(fromFloor, direction);
        }
        return null;
    }
}

// 电梯类
class Elevator {
    private int minFloor;
    private int maxFloor;
    private int currentFloor;
    private String direction;
    private String state;

    public Elevator(int minFloor, int maxFloor) {
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.currentFloor = minFloor;
        this.direction = "IDLE";
        this.state = "Stopped";
    }

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

    public int getCurrentFloor() {
        return currentFloor;
    }

    public String getDirection() {
        return direction;
    }

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

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

// 请求队列类
class RequestQueue {
    private List<Passenger> requests;
    private Elevator elevator;

    public RequestQueue(Elevator elevator) {
        this.requests = new ArrayList<>();
        this.elevator = elevator;
    }

    public void addRequest(Passenger request) {
        if (elevator.isValidFloor(request.getFloor())) {
            if (requests.isEmpty() || !requests.get(requests.size() - 1).equals(request)) {
                requests.add(request);
            }
        }
    }

    public List<Passenger> getRequests() {
        return requests;
    }

    public void removeRequest(Passenger request) {
        requests.remove(request);
    }
}

// 电梯控制类
class ElevatorController {
    private Elevator elevator;
    private RequestQueue requestQueue;
    private ArrayList<Integer> internalRequests;
    private ArrayList<Integer> externalRequests;
    private ArrayList<String> externalDirections;
    private ArrayList<Integer> externalToFloors;

    public ElevatorController(Elevator elevator, RequestQueue requestQueue) {
        this.elevator = elevator;
        this.requestQueue = requestQueue;
        this.internalRequests = new ArrayList<>();
        this.externalRequests = new ArrayList<>();
        this.externalDirections = new ArrayList<>();
        this.externalToFloors = new ArrayList<>();
    }

public void processInput(String input) {
    String internalPattern = "<(\\d+)>";
    String externalPattern = "<(\\d+),(\\d+)>";

    Pattern intPat = Pattern.compile(internalPattern);
    Pattern extPat = Pattern.compile(externalPattern);

    Matcher intMatcher = intPat.matcher(input);
    Integer lastInternalFloor = null;
    while (intMatcher.find()) {
        int floor = Integer.parseInt(intMatcher.group(1));
        if (elevator.isValidFloor(floor) && (lastInternalFloor == null || lastInternalFloor != floor)) {
            internalRequests.add(floor);
            lastInternalFloor = floor;
        }
    }

    Matcher extMatcher = extPat.matcher(input);
    Passenger lastExternalRequest = null;
    while (extMatcher.find()) {
        int fromFloor = Integer.parseInt(extMatcher.group(1));
        int toFloor = Integer.parseInt(extMatcher.group(2));
        String direction = toFloor > fromFloor ? "UP" : "DOWN";
        Passenger newRequest = new Passenger(fromFloor, direction);
        if (lastExternalRequest == null || !lastExternalRequest.equals(newRequest)) {
            requestQueue.addRequest(newRequest);
            externalRequests.add(fromFloor);
            externalDirections.add(direction);
            externalToFloors.add(toFloor);
            // 关键修改:在解析外部请求时,直接将其 toFloor 加入内部请求队尾
            internalRequests.add(toFloor);
            lastExternalRequest = newRequest;
        }
    }
}

    public void run() {
        while (!internalRequests.isEmpty() || !externalRequests.isEmpty()) {
            if (elevator.getDirection().equals("IDLE")) {
                findInitialDirection();
            }
            int targetFloor = getNextTargetFloor();
            moveToFloor(targetFloor);
        }
    }

    private void findInitialDirection() {
        int nearestTarget = Integer.MAX_VALUE;
        if (!externalRequests.isEmpty()) {
            nearestTarget = externalRequests.get(0);
        } else if (!internalRequests.isEmpty()) {
            nearestTarget = internalRequests.get(0);
        }
        if (nearestTarget > elevator.getCurrentFloor()) {
            elevator.setDirection("UP");
        } else if (nearestTarget < elevator.getCurrentFloor()) {
            elevator.setDirection("DOWN");
        }
        System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getDirection());
    }

    private int getNextTargetFloor() {
        int internalTarget = -1;
        int externalTarget = -1;
        String externalDir = "";

        if (!internalRequests.isEmpty()) {
            internalTarget = internalRequests.get(0);
        }
        if (!externalRequests.isEmpty()) {
            externalTarget = externalRequests.get(0);
            externalDir = externalDirections.get(0);
        }

        if (internalTarget != -1 && externalTarget != -1) {
            boolean isInternalSameDirection = isSameDirection(internalTarget);
            boolean isExternalSameDirection = isSameDirection(externalTarget);
            if (isInternalSameDirection && !isExternalSameDirection) {
                return internalRequests.remove(0);
            } else if (!isInternalSameDirection && isExternalSameDirection) {
                elevator.setDirection(externalDir);
                externalDirections.remove(0);
                isExternalSameDirection = externalDir.equals(elevator.getDirection());
                if (!isExternalSameDirection) {
                    changeDirection();
                }
                return externalRequests.remove(0);
            } else if (isInternalSameDirection && isExternalSameDirection) {
                isExternalSameDirection = externalDir.equals(elevator.getDirection());
                if (isInternalSameDirection && isExternalSameDirection) {
                    if (Math.abs(internalTarget - elevator.getCurrentFloor()) <= Math.abs(externalTarget - elevator.getCurrentFloor())) {
                        return internalRequests.remove(0);
                    } else {
                        externalDirections.remove(0);
                        return externalRequests.remove(0);
                    }
                } else {
                    return internalRequests.remove(0);
                }
            } else {
                isExternalSameDirection = externalDir.equals(elevator.getDirection());
                if (!isInternalSameDirection && !isExternalSameDirection) {
                    if (Math.abs(internalTarget - elevator.getCurrentFloor()) <= Math.abs(externalTarget - elevator.getCurrentFloor())) {
                        changeDirection();
                        return internalRequests.remove(0);
                    } else {
                        changeDirection();
                        externalDirections.remove(0);
                        return externalRequests.remove(0);
                    }
                } else {
                    changeDirection();
                    return internalRequests.remove(0);
                }
            }
        } else if (internalTarget != -1) {
            return internalRequests.remove(0);
        } else if (externalTarget != -1) {
            elevator.setDirection(externalDirections.remove(0));
            return externalRequests.remove(0);
        }
        changeDirection();
        return getNextTargetFloor();
    }

    private boolean isSameDirection(int targetFloor) {
        if (elevator.getDirection().equals("UP")) {
            return targetFloor > elevator.getCurrentFloor();
        } else {
            return targetFloor < elevator.getCurrentFloor();
        }
    }

    private void changeDirection() {
        if (elevator.getDirection().equals("UP")) {
            elevator.setDirection("DOWN");
        } else {
            elevator.setDirection("UP");
        }
    }

    private void moveToFloor(int targetFloor) {
        while (elevator.getCurrentFloor() != targetFloor) {
            if (targetFloor > elevator.getCurrentFloor()) {
                elevator.setCurrentFloor(elevator.getCurrentFloor() + 1);
                elevator.setDirection("UP");
            } else {
                elevator.setCurrentFloor(elevator.getCurrentFloor() - 1);
                elevator.setDirection("DOWN");
            }
            System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getDirection());
        }
        System.out.println("Open Door # Floor " + elevator.getCurrentFloor());
        System.out.println("Close Door");
        removeProcessedRequests(targetFloor);
    }
private void removeProcessedRequests(int floor) {
    if (!internalRequests.isEmpty() && internalRequests.get(0) == floor) {
        internalRequests.remove(0);
    }
    else if (!externalRequests.isEmpty() && externalRequests.get(0) == floor) {
        externalToFloors.remove(0);
        externalRequests.remove(0);
    }
}

}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int minFloor = scanner.nextInt();
        int maxFloor = scanner.nextInt();
        scanner.nextLine();

        StringBuilder input = new StringBuilder();
        String line;
        while (true) {
            line = scanner.nextLine();
            if (line.trim().equalsIgnoreCase("end")) {
                break;
            }
            input.append(line).append(" ");
        }

        Elevator elevator = new Elevator(minFloor, maxFloor);
        RequestQueue requestQueue = new RequestQueue(elevator);
        ElevatorController controller = new ElevatorController(elevator, requestQueue);
        controller.processInput(input.toString());
        controller.run();

        scanner.close();
    }
}    

这一次作业要求乘客请外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>,并且对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾),再将乘客请求类删除,添加乘客类。对于外部楼层请求输入格式的修改,我们只要将正则表达式格式修改,再添加判断外部请求方向的方法,最后将外部请求的目标楼层加入内部请求队列的队尾即可,对于代码的修改并不多。

类图:

复杂度图

其中sourcemonitor图中解释如下:
行数:335 行
语句数:224 条
分支语句百分比:21.0% ,意味着约五分之一语句是分支逻辑(if - else)
方法调用语句数:135 条
注释行百分比:1.2% ,即代码中注释占比较少
类和接口数量:1 个
每个类的平均方法数:26.00 个
每个方法的平均语句数:8.31 条
最复杂方法的行号:307 行,方法为 Main.main ()
最大复杂度:3 ,方法为 Main.main ()
最深代码块行号:229 行
最大代码块深度:5
平均代码块深度:1.54
平均复杂度:3.00

我个人认为第三次电梯作业最简单,这次作业对代码的修改和完善比较少,只是在原代码的基础上修改了输入格式和添加了一个判断外部请求方向的方法,进行了功能上的完善和迭代,所以代码的复杂度没有多大变化。
观察类图和复杂度图,发现平均复杂度和平均代码深度都没有变化,复杂度最大的方法依旧是 Main.main (),其他方面都没有太大的变化,但还是存在许多方面的问题,和第二次作业基本相同,还是使用了大量的if-else逻辑嵌套结构,这表明许多方法内部逻辑过于繁杂,需要对这些逻辑进行梳理。其次就是平均深度不均衡,并且部分方法的复杂度相较于第二次作业加深,这就需要将各个方法进行归纳,减少分支和循环结构的使用。最后部分类设计不合理,类的职责不单一,违背了单一职责原理等,还需要将这些类继续拆分。

三、踩坑总结
第一次作业时,最开始不会用正则表达式,pta提交一直显示编译错误,花了两天时间才发现错误在哪,结果又出现答案错误的问题,能过一开始给的样例,提交后却出现答案错误,一直没有搞懂电梯运行的算法逻辑,到底什么时候内部请求优先出队,如何定义同向,最后花费五天半终于写对了。
第二次作业时,将电梯类分解后,我的代码提交结果一直显示运行超时,最后发现是前一次作业时代码有漏洞,有一种情况未考虑到,导致运行时进入了死循环,但将算法补全后出现了代码长度超出限制的问题,说明我在算法逻辑设计上有许多重复的无意义的判断语句,在调试了几天后才终于通过。
第三次作业时,没有仔细看题目需求,将外部请求的目标楼层放到了外部请求队列的队尾。

四、改进建议
重复代码提取:processInput 方法里正则表达式的模式定义和匹配逻辑与 Passenger.parse 方法重复,可将其提取成一个独立方法以避免重复。
注释和文档:虽然代码中有部分注释,但部分复杂逻辑(如 getNextTargetFloor 方法)可添加更多注释来增强可读性。
类和方法的职责划分:ElevatorController 类的职责较为繁杂,可考虑将部分逻辑拆分到其他类中,像请求排序和筛选的逻辑可单独封装。
请求处理:在处理请求时,ArrayList 的 remove(0) 操作时间复杂度为 O(n)
可使用 LinkedList 来优化,因为其 removeFirst 操作时间复杂度为 O(1)
重复请求检查:在添加请求时,每次都要遍历列表来检查是否重复,可使用 Set 来存储请求,以实现 O(1)的查找时间复杂度。
输入验证:在 Main 方法中,对于用户输入的 minFloor 和 maxFloor 未进行有效性检查,可能会引发异常。
正则匹配失败处理:在 processInput 方法中,若正则匹配失败,没有相应的错误处理机制。

五、总结
在这三次题目集中,我学会了:
1,正确使用正则表达式。
2,掌握了ArrayList 的基础用法,学会了使用add()、remove() 等方法来添加和删除动态数组中的元素。
3,学会了定义类的属性,知道了如何处理和设计类与类之间的关系。

这三次作业同时让我认识到了自己在Java这门语言上的不足:
1,在写算法逻辑时,频繁的使用if-else结构,严重降低了代码的可读性。
2,代码中的注释太少,应该添加更多的注释来解释每一步的操作意图。
3,数据结构的选择不合理,在处理请求队列时,使用 ArrayList 进行频繁的 remove(0) 操作效率较低。
4,没有做到模块化处理,代码中存在一些可以复用的部分,却没有进行适当的抽象和封装

posted @ 2025-04-20 18:02  好喜欢学校  阅读(40)  评论(0)    收藏  举报