1.前言

1.知识点总结

第一道电梯题采用单类实现,集中处理所有功能,虽然简单但耦合度高。第一次迭代遵循单一职责原则,拆分为电梯类、请求类、队列类和控制类,实现功能解耦,并增加请求去重机制。第二次迭代引入乘客类替代请求类,采用"源楼层-目的楼层"的请求格式。本次电梯算法迭代作业涉及了Java的基础语法知识、类与对象、异常处理、字符串、包装类(Integer类等)、枚举与泛型、正则表达式、集合框架等Java编程知识以及单一职责原则等Java设计准则。该次作业充分体现了Java面向对象的设计思想及对解决生活实际问题的重大作用。

2. 题目量及难度

对于Java初学者来说,这几次题目集的题目量恰到好处,简单题有助于巩固所学,电梯题虽然困难,但有助于精进。

2.设计与分析

(1)第一次电梯题源码

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

enum Direction {
    UP, DOWN, IDLE
}

enum State {
    STOPPED, MOVING, OPENING, CLOSING
}

class Elevator {
    private final int minFloor;
    private final int maxFloor;
    private int currentFloor;
    private Direction direction;
    private State state;
    private final List<Integer> internalRequests;
    private final Map<Integer, Direction> externalRequests;

    public Elevator(int minFloor, int maxFloor) {
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.currentFloor = minFloor;
        this.direction = Direction.IDLE;
        this.state = State.STOPPED;
        this.internalRequests = new ArrayList<>();
        this.externalRequests = new HashMap<>();
    }

    public void addInternalRequest(int floor) {
        if (floor >= minFloor && floor <= maxFloor && !internalRequests.contains(floor)) {
            internalRequests.add(floor);
        }
    }

    public void addExternalRequest(int floor, Direction dir) {
        if (floor >= minFloor && floor <= maxFloor) {
            externalRequests.put(floor, dir);
        }
    }

    public void processRequests() {
        while (!internalRequests.isEmpty() || !externalRequests.isEmpty()) {
            // Determine direction if idle
            if (direction == Direction.IDLE) {
                determineInitialDirection();
            }

            // Move one floor in current direction
            if (direction == Direction.UP) {
                currentFloor++;
            } else if (direction == Direction.DOWN) {
                currentFloor--;
            }

            // Print movement
            System.out.println("Current Floor: " + currentFloor + " Direction: " + direction);

            // Check if need to stop at this floor
            boolean needStop = false;

            // Check internal requests
            if (internalRequests.contains(currentFloor)) {
                needStop = true;
                internalRequests.remove((Integer) currentFloor);
            }

            // Check external requests
            if (externalRequests.containsKey(currentFloor)) {
                Direction reqDir = externalRequests.get(currentFloor);
                if (reqDir == direction || 
                    (direction == Direction.UP && currentFloor == maxFloor) || 
                    (direction == Direction.DOWN && currentFloor == minFloor)) {
                    needStop = true;
                    externalRequests.remove(currentFloor);
                }
            }

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

            // Check if should change direction
            checkDirectionChange();
        }

        // No more requests, go idle
        direction = Direction.IDLE;
        state = State.STOPPED;
    }

    private void determineInitialDirection() {
        if (!internalRequests.isEmpty()) {
            int target = internalRequests.get(0);
            direction = target > currentFloor ? Direction.UP : Direction.DOWN;
        } else if (!externalRequests.isEmpty()) {
            int nearestFloor = externalRequests.keySet().iterator().next();
            Direction reqDir = externalRequests.get(nearestFloor);
            if (reqDir == Direction.UP) {
                direction = nearestFloor > currentFloor ? Direction.UP : Direction.DOWN;
            } else {
                direction = nearestFloor < currentFloor ? Direction.DOWN : Direction.UP;
            }
        }
    }

    private void checkDirectionChange() {
        if (direction == Direction.UP) {
            boolean hasUpRequests = false;
            
            // Check internal requests above current floor
            for (int floor : internalRequests) {
                if (floor > currentFloor) {
                    hasUpRequests = true;
                    break;
                }
            }
            
            // Check external UP requests above current floor
            if (!hasUpRequests) {
                for (Map.Entry<Integer, Direction> entry : externalRequests.entrySet()) {
                    if (entry.getKey() > currentFloor && entry.getValue() == Direction.UP) {
                        hasUpRequests = true;
                        break;
                    }
                }
            }
            
            if (!hasUpRequests) {
                // Check if there are any requests in opposite direction
                boolean hasDownRequests = false;
                
                // Check internal requests below current floor
                for (int floor : internalRequests) {
                    if (floor < currentFloor) {
                        hasDownRequests = true;
                        break;
                    }
                }
                
                // Check external DOWN requests below current floor
                if (!hasDownRequests) {
                    for (Map.Entry<Integer, Direction> entry : externalRequests.entrySet()) {
                        if (entry.getKey() < currentFloor && entry.getValue() == Direction.DOWN) {
                            hasDownRequests = true;
                            break;
                        }
                    }
                }
                
                if (hasDownRequests) {
                    direction = Direction.DOWN;
                } else {
                    // Check if there are any external UP requests below (people waiting to go up)
                    for (Map.Entry<Integer, Direction> entry : externalRequests.entrySet()) {
                        if (entry.getKey() < currentFloor && entry.getValue() == Direction.UP) {
                            direction = Direction.DOWN;
                            break;
                        }
                    }
                }
            }
        } else if (direction == Direction.DOWN) {
            boolean hasDownRequests = false;
            
            // Check internal requests below current floor
            for (int floor : internalRequests) {
                if (floor < currentFloor) {
                    hasDownRequests = true;
                    break;
                }
            }
            
            // Check external DOWN requests below current floor
            if (!hasDownRequests) {
                for (Map.Entry<Integer, Direction> entry : externalRequests.entrySet()) {
                    if (entry.getKey() < currentFloor && entry.getValue() == Direction.DOWN) {
                        hasDownRequests = true;
                        break;
                    }
                }
            }
            
            if (!hasDownRequests) {
                // Check if there are any requests in opposite direction
                boolean hasUpRequests = false;
                
                // Check internal requests above current floor
                for (int floor : internalRequests) {
                    if (floor > currentFloor) {
                        hasUpRequests = true;
                        break;
                    }
                }
                
                // Check external UP requests above current floor
                if (!hasUpRequests) {
                    for (Map.Entry<Integer, Direction> entry : externalRequests.entrySet()) {
                        if (entry.getKey() > currentFloor && entry.getValue() == Direction.UP) {
                            hasUpRequests = true;
                            break;
                        }
                    }
                }
                
                if (hasUpRequests) {
                    direction = Direction.UP;
                } else {
                    // Check if there are any external DOWN requests above (people waiting to go down)
                    for (Map.Entry<Integer, Direction> entry : externalRequests.entrySet()) {
                        if (entry.getKey() > currentFloor && entry.getValue() == Direction.DOWN) {
                            direction = Direction.UP;
                            break;
                        }
                    }
                }
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        int minFloor = Integer.parseInt(scanner.nextLine());
        int maxFloor = Integer.parseInt(scanner.nextLine());
        
        Elevator elevator = new Elevator(minFloor, maxFloor);
        
        while (true) {
            String input = scanner.nextLine().trim();
            if (input.equalsIgnoreCase("end")) {
                break;
            }
            
            if (input.startsWith("<") && input.endsWith(">")) {
                String content = input.substring(1, input.length() - 1);
                if (content.contains(",")) {
                    // External request
                    String[] parts = content.split(",");
                    try {
                        int floor = Integer.parseInt(parts[0].trim());
                        Direction dir = Direction.valueOf(parts[1].trim());
                        elevator.addExternalRequest(floor, dir);
                    } catch (Exception e) {
                        // Invalid input, ignore
                    }
                } else {
                    // Internal request
                    try {
                        int floor = Integer.parseInt(content.trim());
                        elevator.addInternalRequest(floor);
                    } catch (Exception e) {
                        // Invalid input, ignore
                    }
                }
            }
        }
        
        elevator.processRequests();
    }
}
类图

image

(2)分析结果

image
根据SourceMontor分析该电梯调度代码实现了基本功能但存在明显的设计缺陷和优化空间。主要还是因为我的代码中Elevator类承担了过多职责,既管理电梯运行状态,又处理请求队列,还包含调度算法。违背了单一职责原则。其次,从代码质量报告来看,Main类包含309行代码却只有11.3%的注释率,关键算法缺乏文档说明,这导致我在修改代码的时候花费了许多时间,不利于代码的维护与改进。最后,因为main()方法复杂度高达9,最大块深度达到5层,这些指标反映出代码可读性和可维护性较差。

(3)第一次迭代源码

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

enum Direction {
    UP, DOWN, IDLE
}

enum State {
    STOPPED, MOVING, OPENING, CLOSING
}

class Request {
    private final Integer floor;
    private final Direction direction;
    private final boolean isInternal;

    public Request(Integer floor) {
        this.floor = floor;
        this.direction = null;
        this.isInternal = true;
    }

    public Request(Integer floor, Direction direction) {
        this.floor = floor;
        this.direction = direction;
        this.isInternal = false;
    }

    public Integer getFloor() {
        return floor;
    }

    public Direction getDirection() {
        return direction;
    }

    public boolean isInternal() {
        return isInternal;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Request request = (Request) o;
        return isInternal == request.isInternal &&
                Objects.equals(floor, request.floor) &&
                direction == request.direction;
    }

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

class RequestQueue {
    private final Set<Request> internalRequests;
    private final Set<Request> externalRequests;

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

    public void addRequest(Request request) {
        if (request.isInternal()) {
            internalRequests.add(request);
        } else {
            externalRequests.add(request);
        }
    }

    public boolean removeInternalRequest(int floor) {
        return internalRequests.removeIf(req -> req.getFloor() == floor);
    }

    public boolean removeExternalRequest(int floor, Direction direction) {
        return externalRequests.removeIf(req -> req.getFloor() == floor && req.getDirection() == direction);
    }

    public Set<Request> getInternalRequests() {
        return new HashSet<>(internalRequests);
    }

    public Set<Request> getExternalRequests() {
        return new HashSet<>(externalRequests);
    }

    public boolean isEmpty() {
        return internalRequests.isEmpty() && externalRequests.isEmpty();
    }
}

class Elevator {
    private final int minFloor;
    private final int maxFloor;
    private int currentFloor;
    private Direction direction;
    private State state;

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

    public int getCurrentFloor() {
        return currentFloor;
    }

    public Direction getDirection() {
        return direction;
    }

    public State getState() {
        return state;
    }

    public void moveUp() {
        if (currentFloor < maxFloor) {
            currentFloor++;
            direction = Direction.UP;
            state = State.MOVING;
        }
    }

    public void moveDown() {
        if (currentFloor > minFloor) {
            currentFloor--;
            direction = Direction.DOWN;
            state = State.MOVING;
        }
    }

    public void openDoor() {
        state = State.OPENING;
    }

    public void closeDoor() {
        state = State.CLOSING;
    }

    public void stop() {
        state = State.STOPPED;
        direction = Direction.IDLE;
    }

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

class ElevatorController {
    private final Elevator elevator;
    private final RequestQueue requestQueue;
    private final int minFloor;
    private final int maxFloor;

    public ElevatorController(int minFloor, int maxFloor) {
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.elevator = new Elevator(minFloor, maxFloor);
        this.requestQueue = new RequestQueue();
    }

    public void addRequest(Request request) {
        if (isValidRequest(request)) {
            requestQueue.addRequest(request);
        }
    }

    private boolean isValidRequest(Request request) {
        if (request.getFloor() < minFloor || request.getFloor() > maxFloor) {
            return false;
        }
        return true;
    }

    public void processRequests() {
        while (!requestQueue.isEmpty()) {
            // Determine direction if idle
            if (elevator.getDirection() == Direction.IDLE) {
                determineInitialDirection();
            }

            // Move one floor in current direction
            if (elevator.getDirection() == Direction.UP) {
                elevator.moveUp();
            } else if (elevator.getDirection() == Direction.DOWN) {
                elevator.moveDown();
            }

            // Print movement
            System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getDirection());

            // Check if need to stop at this floor
            boolean needStop = false;

            // Check internal requests
            if (requestQueue.getInternalRequests().stream().anyMatch(req -> req.getFloor() == elevator.getCurrentFloor())) {
                needStop = true;
                requestQueue.removeInternalRequest(elevator.getCurrentFloor());
            }

            // Check external requests
            if (requestQueue.getExternalRequests().stream().anyMatch(req -> 
                req.getFloor() == elevator.getCurrentFloor() && 
                (req.getDirection() == elevator.getDirection() || 
                 (elevator.getDirection() == Direction.UP && elevator.getCurrentFloor() == maxFloor) || 
                 (elevator.getDirection() == Direction.DOWN && elevator.getCurrentFloor() == minFloor)))) {
                needStop = true;
                requestQueue.removeExternalRequest(elevator.getCurrentFloor(), elevator.getDirection());
            }

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

            // Check if should change direction
            checkDirectionChange();
        }

        // No more requests, go idle
        elevator.stop();
    }

    private void determineInitialDirection() {
        Set<Request> internalRequests = requestQueue.getInternalRequests();
        Set<Request> externalRequests = requestQueue.getExternalRequests();

        if (!internalRequests.isEmpty()) {
            int target = internalRequests.iterator().next().getFloor();
            elevator.setDirection(target > elevator.getCurrentFloor() ? Direction.UP : Direction.DOWN);
        } else if (!externalRequests.isEmpty()) {
            Request firstRequest = externalRequests.iterator().next();
            int floor = firstRequest.getFloor();
            Direction reqDir = firstRequest.getDirection();
            
            if (reqDir == Direction.UP) {
                elevator.setDirection(floor > elevator.getCurrentFloor() ? Direction.UP : Direction.DOWN);
            } else {
                elevator.setDirection(floor < elevator.getCurrentFloor() ? Direction.DOWN : Direction.UP);
            }
        }
    }

    private void checkDirectionChange() {
        if (elevator.getDirection() == Direction.UP) {
            boolean hasUpRequests = hasUpRequests();
            
            if (!hasUpRequests) {
                boolean hasDownRequests = hasDownRequests();
                
                if (hasDownRequests) {
                    elevator.setDirection(Direction.DOWN);
                } else {
                    // Check if there are any external UP requests below
                    boolean hasUpRequestsBelow = requestQueue.getExternalRequests().stream()
                            .anyMatch(req -> req.getFloor() < elevator.getCurrentFloor() && req.getDirection() == Direction.UP);
                    if (hasUpRequestsBelow) {
                        elevator.setDirection(Direction.DOWN);
                    }
                }
            }
        } else if (elevator.getDirection() == Direction.DOWN) {
            boolean hasDownRequests = hasDownRequests();
            
            if (!hasDownRequests) {
                boolean hasUpRequests = hasUpRequests();
                
                if (hasUpRequests) {
                    elevator.setDirection(Direction.UP);
                } else {
                    // Check if there are any external DOWN requests above
                    boolean hasDownRequestsAbove = requestQueue.getExternalRequests().stream()
                            .anyMatch(req -> req.getFloor() > elevator.getCurrentFloor() && req.getDirection() == Direction.DOWN);
                    if (hasDownRequestsAbove) {
                        elevator.setDirection(Direction.UP);
                    }
                }
            }
        }
    }

    private boolean hasUpRequests() {
        // Check internal requests above current floor
        boolean hasInternalUp = requestQueue.getInternalRequests().stream()
                .anyMatch(req -> req.getFloor() > elevator.getCurrentFloor());
        
        // Check external UP requests above current floor
        boolean hasExternalUp = requestQueue.getExternalRequests().stream()
                .anyMatch(req -> req.getFloor() > elevator.getCurrentFloor() && req.getDirection() == Direction.UP);
        
        return hasInternalUp || hasExternalUp;
    }

    private boolean hasDownRequests() {
        // Check internal requests below current floor
        boolean hasInternalDown = requestQueue.getInternalRequests().stream()
                .anyMatch(req -> req.getFloor() < elevator.getCurrentFloor());
        
        // Check external DOWN requests below current floor
        boolean hasExternalDown = requestQueue.getExternalRequests().stream()
                .anyMatch(req -> req.getFloor() < elevator.getCurrentFloor() && req.getDirection() == Direction.DOWN);
        
        return hasInternalDown || hasExternalDown;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        int minFloor = Integer.parseInt(scanner.nextLine());
        int maxFloor = Integer.parseInt(scanner.nextLine());
        
        ElevatorController controller = new ElevatorController(minFloor, maxFloor);
        Request lastRequest = null;
        
        while (true) {
            String input = scanner.nextLine().trim();
            if (input.equalsIgnoreCase("end")) {
                break;
            }
            
            if (input.startsWith("<") && input.endsWith(">")) {
                String content = input.substring(1, input.length() - 1);
                Request currentRequest = null;
                
                if (content.contains(",")) {
                    // External request
                    String[] parts = content.split(",");
                    try {
                        int floor = Integer.parseInt(parts[0].trim());
                        Direction dir = Direction.valueOf(parts[1].trim());
                        currentRequest = new Request(floor, dir);
                    } catch (Exception e) {
                        // Invalid input, ignore
                        continue;
                    }
                } else {
                    // Internal request
                    try {
                        int floor = Integer.parseInt(content.trim());
                        currentRequest = new Request(floor);
                    } catch (Exception e) {
                        // Invalid input, ignore
                        continue;
                    }
                }
                
                // Filter duplicate requests
                if (currentRequest != null && !currentRequest.equals(lastRequest)) {
                    controller.addRequest(currentRequest);
                    lastRequest = currentRequest;
                }
            }
        }
        
        controller.processRequests();
    }
}
类图

image

(4)分析结果

image
根据SourceMontor分析,我的代码Main.java文件增长到367行,包含7个类和接口,平均每个类4.71个方法,方法平均语句数降至4.33,这些数据表明代码已进行了一定程度的职责分离。但最复杂方法main()的复杂度反而从9上升到11,最大块深度达到6层,注释比例暴跌至5.2%。第一次题目的基础上,我们将第一次作业中的电梯类分为电梯类、乘客请求类、队列类以及控制类。实现了单一控制原则,但是此次问题的难点就在于设计类与类之间的关系。但是由于多类的设计,代码的可维护性变高,尽管未添加太多的注释。
(5)第二次迭代源码

点击查看代码

类图

image

(6)分析结果

image
根据SourceMontor分析,我的代码中Main.java文件共有384行代码,209个语句,平均每个方法有4.41个语句,最复杂的方法是Main.main(),有269行代码。代码的圈复杂度为11,表明代码的逻辑较为复杂,可能存在多个分支和循环。代码的注释率较低,只有5.2%。总体来说代码的难度变高了,但是因为多类设计,代码的可维护性比之前的更强了。但是由于未添加多少注释的原因,代码的可读性还是未能提高。

3.踩坑心得

1.在第一次作业中,未能清晰的分析这道题的代码逻辑,导致在编写代码的时候许多功能出现错误,编译器输出答案错误,其次就是不太熟练的使用正则表达式,也阻碍了我顺利编写代码。
2.在第一次迭代中,由于多类的设计以及类与类之间的设计和它们的关系调用较为复杂。导致出现答案错误以及非0返回等问题。
3.在写这种较为复杂的题目的时候,未添加注释导致在代码的修改与维护的过程中较为复杂,无疑添加了一些不必要的麻烦
4.在使用数据结构时,要深入理解其操作方法和特性。对于 LinkedList 这样的动态列表,在进行删除操作时,必须使用迭代器来避免索引越界问题。

4.改进建议

1.增加注释和文档:在代码的关键部分添加详细的注释,解释代码的功能、逻辑和设计决策。这不仅有助于他人理解代码,也有助于自己在将来回顾和维护代码时能够快速上手。
2.优化类设计:虽然已经进行了职责分离,但还可以进一步优化类的设计。例如,可以考虑将电梯的运动逻辑和请求处理逻辑进一步分离,使得每个类的职责更加单一,从而提高代码的可读性和可维护性。
3.简化复杂方法:对于复杂度较高的方法,如Main.main(),可以考虑将其拆分为多个小方法,每个方法负责一个具体的任务。这有助于降低方法的复杂度,提高代码的可读性和可维护性。
4.深入理解数据结构:在使用数据结构时,需要深入理解其操作方法和特性。例如,在使用LinkedList进行删除操作时,需要使用迭代器来避免索引越界问题。理解数据结构的特性可以帮助我们更有效地使用它们,避免常见的错误。

5.总结

1.问题总结

在第一次迭代中,我意识到了单一职责原则的重要性,并尝试通过拆分Elevator类来实现功能解耦。然而,由于对类与类之间关系的设计不够清晰,导致代码的可读性和可维护性仍有待提高。在第二次迭代中,我引入了乘客类来替代请求类,这不仅简化了请求格式,也提高了代码的可维护性。尽管如此,由于注释不足,代码的可读性仍然不高,这在后续的代码修改和维护中给我带来了额外的挑战。

2.学习总结

通过本次迭代问题,我学会了正确使用正则表达式。掌握了ArrayList 的基础用法。以及单类与多类的设计。

3.反思与建议

三次电梯难题锻炼了我的逻辑思维能力,我学会将实际问题通过编程来逐步实现,对于编程有了更深的理解。但我也意识到我在java学习上的不足。让我明白我掌握的不过只是基础中的基础,我更应该多投身于java的学习,认真上课。