第一次博客作业——关于电梯迭代的分析与总结

第一次Blog作业——单部电梯调度

一、前言

  本学期,我们开设了面向对象程序设计这门课程,相较于第一学期学习的第一门语言——C语言来说,它的难度更大,语法更多。与之相匹配的是难度更大的题目集,作为第一次迭代作业——单部电梯调度,它向我们很好地展示了这一点。下面我将开始分析这次的迭代作业。

二、设计与分析

 1、第一次迭代设计与分析

  (1)代码展示

第一次电梯迭代

查看代码
  import java.util.LinkedList;
import java.util.ArrayList;
import java.util.Scanner;
enum Direction {
	UP, DOWN
}
enum ElevatorStatus {
	MOVING, STOP, IDLE
}

//电梯类
class Elevator {
	// 内部类,表示外部请求
	private static class ExternalRequest {
		int floor = 0;
		Direction direction = null;

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

	private int maxFloor = 0;
	private int minFloor = 0;
	private int currentFloor = 0;
	private Direction direction;
	private LinkedList<Integer> internalRequests;
	private LinkedList<ExternalRequest> externalRequests;
	private ElevatorStatus status;

	// 构造函数,初始化电梯的最大楼层、最小楼层、当前楼层、方向、请求队列和状态
	public Elevator(int maxFloor, int minFloor) {
		this.maxFloor = maxFloor;
		this.minFloor = minFloor;
		this.currentFloor = minFloor;
		this.direction = Direction.UP;
		this.internalRequests = new LinkedList<>();
		this.externalRequests = new LinkedList<>();
		this.status = ElevatorStatus.IDLE;
	}

	// 检查楼层是否在有效范围内
	private boolean isValidFloor(int floor) {
		return floor >= minFloor && floor <= maxFloor;
	}

	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 void addInternalRequest(int floor) {
		if (isValidFloor(floor)) {
			internalRequests.add(floor);
			if (status == ElevatorStatus.IDLE) {
				status = ElevatorStatus.MOVING;
			}
		}
	}

	// 添加外部请求
	public void addExternalRequest(int floor, Direction direction) {
		if (isValidFloor(floor)) {
			externalRequests.add(new ExternalRequest(floor, direction));
			if (status == ElevatorStatus.IDLE) {
				status = ElevatorStatus.MOVING;
			}
		}
	}

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

	public LinkedList<ExternalRequest> getExternalRequests() {
		return externalRequests;
	}

	// 查找下一个要处理的请求楼层
	public int findNextRequest() {
		int nextFloor = -1;
		Integer internalNext = internalRequests.isEmpty() ? null : internalRequests.peek();
		ExternalRequest externalNext = externalRequests.isEmpty() ? null : externalRequests.peek();

		if (externalNext != null && internalNext != null) {
			if (direction == Direction.UP) {
				if(internalNext == currentFloor || externalNext.floor == currentFloor) {
					if(internalNext == currentFloor) {
						internalRequests.poll();
						return findNextRequest();
					}else {
						if(externalNext.direction == Direction.UP) {
							externalRequests.poll();
							return findNextRequest();
						}else {
							nextFloor = externalNext.floor;
							direction = externalNext.direction;
						}
					}
				}
				if (internalNext > currentFloor) {
					nextFloor = internalNext;
					if (externalNext.floor > currentFloor && externalNext.floor < internalNext
							&& externalNext.direction == Direction.UP) {
						nextFloor = externalNext.floor;
					}
				} else if (externalNext.floor > currentFloor) {
					nextFloor = externalNext.floor;
					direction = externalNext.direction;
				}
				if (nextFloor == -1) {
					direction = Direction.DOWN;
					return findNextRequest();
				}
			} else if (direction == Direction.DOWN) {
				if(internalNext == currentFloor || externalNext.floor == currentFloor) {
					if(internalNext == currentFloor) {
						internalRequests.poll();
						return findNextRequest();
					}else {
						if(externalNext.direction == Direction.DOWN) {
							externalRequests.poll();
							return findNextRequest();
						}else {
							nextFloor = externalNext.floor;
							direction = externalNext.direction;
						}
					}
				}
				if (internalNext < currentFloor) {
					nextFloor = internalNext;
					if (externalNext.floor < currentFloor && externalNext.floor > internalNext
							&& externalNext.direction == Direction.DOWN) {
						nextFloor = externalNext.floor;
					}
				} else if (externalNext.floor < currentFloor) {
					nextFloor = externalNext.floor;
					direction = externalNext.direction;
				}
				if (nextFloor == -1) {
					direction = Direction.UP;
					return findNextRequest();
				}
			}
		} else if (externalNext != null) {
			if (direction == Direction.UP) {
				if(externalNext.floor == currentFloor) {
					if(direction == externalNext.direction) {
						externalRequests.poll();
						return findNextRequest();
					}else {
						nextFloor = externalNext.floor;
						direction = externalNext.direction;
					}
				}
				if (externalNext.floor > currentFloor) {
					nextFloor = externalNext.floor;
					direction = externalNext.direction;
				}
				if (nextFloor == -1) {
					direction = Direction.DOWN;
				}
			} else if (direction == Direction.DOWN) {
				if(externalNext.floor == currentFloor) {
					if(direction == externalNext.direction) {
						externalRequests.poll();
						return findNextRequest();
					}else {
						nextFloor = externalNext.floor;
						direction = externalNext.direction;
					}
				}
				if (externalNext.floor < currentFloor) {
					nextFloor = externalNext.floor;
					direction = externalNext.direction;
				}
				if (nextFloor == -1) {
					direction = Direction.UP;
				}
			}
		} else if (internalNext != null) {
			if (direction == Direction.UP) {
				if(internalNext == currentFloor) {
					internalRequests.poll();
					return findNextRequest();
				}
				if (internalNext > currentFloor) {
					nextFloor = internalNext;
				}
				if (nextFloor == -1) {
					direction = Direction.DOWN;
				}
			} else if (direction == Direction.DOWN) {
				if(internalNext == currentFloor) {
					internalRequests.poll();
					return findNextRequest();
				}
				if (internalNext < currentFloor) {
					nextFloor = internalNext;
				}
				if (nextFloor == -1) {
					direction = Direction.UP;
				}
			}
		}
		return nextFloor;
	}

	// 处理请求
	public void processRequests() {
		int nextFloor = findNextRequest();
		if (nextFloor != -1) {
			status = ElevatorStatus.MOVING;
			if (currentFloor != nextFloor) {
				while (currentFloor != nextFloor) {
					if (currentFloor < nextFloor) {
						currentFloor++;
						System.out.println("Current Floor: " + currentFloor + " Direction: UP");
					} else {
						currentFloor--;
						System.out.println("Current Floor: " + currentFloor + " Direction: DOWN");
					}
				}
				System.out.println("Open Door # Floor " + currentFloor);
				System.out.println("Close Door");
			}else {
				System.out.println("Open Door # Floor " + currentFloor);
				System.out.println("Close Door");
			}
			// 删除对应队列的队头
			if (!internalRequests.isEmpty() && internalRequests.peek() == nextFloor) {
				internalRequests.poll();
			}
			if (!externalRequests.isEmpty() && externalRequests.peek().floor == nextFloor) {
				externalRequests.poll();
			}

			if (internalRequests.isEmpty() && externalRequests.isEmpty()) {
				status = ElevatorStatus.IDLE;
			} else {
				status = ElevatorStatus.MOVING;
			}
		}
	}

	public ElevatorStatus getStatus() {
		return status;
	}
}

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String data;
        int floor = 0;
        Direction direction = null;
        int minFloor, maxFloor;
        String request = "";
        ArrayList<String> list = new ArrayList<>();

        data = input.next();
        while (!data.equalsIgnoreCase("End")) {
            list.add(data);
            data = input.next();
        }

        minFloor = Integer.parseInt(list.get(0));
        maxFloor = Integer.parseInt(list.get(1));
        Elevator elevator = new Elevator(maxFloor, minFloor);

        for (int i = 2; i < list.size(); i++) {
            request = list.get(i);
            if (request.contains(",")) {
                if (!request.matches("<\\d+,\\s*(UP|DOWN)>")) {
                    System.out.println("Wrong Format");
                }
                String[] parts = request.replaceAll("[<>]", "").split(",");
                floor = Integer.parseInt(parts[0].trim());
                direction = Direction.valueOf(parts[1].trim().toUpperCase());
                elevator.addExternalRequest(floor, direction);
            } else {
                if (!request.matches("<\\d+>")) {
                    System.out.println("Wrong Format");
                }
                floor = Integer.parseInt(request.replaceAll("[<>]", ""));
                elevator.addInternalRequest(floor);
            }
        }
        System.out.println("Current Floor: " + elevator.getMinFloor() + " Direction: UP");
        while (!elevator.getInternalRequests().isEmpty()
                || !elevator.getExternalRequests().isEmpty()) {
            elevator.processRequests();
        }
        input.close();
    }
}    

 (2)类间关系分析

  • Main类:用于获取用户输入信息,并将信息进行分隔处理以方便参数传入方法。
  • Elevator类:与ExternalRequest类相关联,用于控制与电梯有关的一切行为。
  • ExternalRequest类:将外部信息单独划分成一个类,便于LinkedList的使用。
  • 枚举:用于表示电梯状态及运行方向。

  (3)SourceMoniter分析结果:

  1. 基本信息
    • 代码行数:313 行
    • 语句数:201 条
  2. 代码结构相关
    • 类和接口数量:4 个(包括Elevator类、内部类ExternalRequest、Direction枚举、ElevatorStatus枚举 )
    • 平均每个类的方法数:3.00
  3. 复杂度相关
    • 平均复杂度:5.00
    • 最复杂方法
      • 方法名:Main.main()
      • 复杂度值:262 。Elevator.findNextRequest方法逻辑分支、嵌套多,大量条件判断根据电梯方向、请求情况确定处理楼层,导致复杂度高。
    • Kiviat Graph(雷达图):展示平均复杂度、平均深度、最大深度、最大复杂度、平均每个方法的语句数、注释比例等综合指标。
    • Block Histogram(柱状图):横坐标为代码块深度,纵坐标为语句数,显示较低深度(0 - 3)代码块语句较多。

  体会:第一次迭代的代码复杂度远高于平均水平,究其原因是没有使用多类设计以及单一原则处理(SPR),其中findNextRequest方法在判断下一请求的过程中使用了大量if-else嵌套调用,且有大量重复代码,造成时间复杂度过高。

2、第二次迭代设计与分析

  (1)代码展示

查看代码
  import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Scanner;

enum Direction {
    UP, DOWN
}

enum ElevatorStatus {
    MOVING, STOP, IDLE
}

class ExternalRequest {
    int floor;
    Direction direction;

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

class RequestQueue {
    private LinkedList<Integer> internalRequests;
    private LinkedList<ExternalRequest> externalRequests;

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

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

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

    public int findNextRequest(Elevator elevator) {
        int nextFloor = -1;
        Integer internalNext = internalRequests.isEmpty() ? null : internalRequests.peek();
        ExternalRequest externalNext = externalRequests.isEmpty() ? null : externalRequests.peek();
        if (externalNext != null && internalNext != null) {
            if (elevator.getDirection() == Direction.UP) {
                if (internalNext == elevator.getCurrentFloor() || externalNext.floor == elevator.getCurrentFloor()) {
                    if (internalNext == elevator.getCurrentFloor()) {
                        internalRequests.poll();
                        return findNextRequest(elevator);
                    } else {
                        if (externalNext.direction == Direction.UP) {
                            externalRequests.poll();
                            return findNextRequest(elevator);
                        } else {
                            nextFloor = externalNext.floor;
                            elevator.setDirection(externalNext.direction);
                        }
                    }
                }
                if (internalNext > elevator.getCurrentFloor()) {
                    nextFloor = internalNext;
                    if (externalNext.floor > elevator.getCurrentFloor() && externalNext.floor < internalNext
                            && externalNext.direction == Direction.UP) {
                        nextFloor = externalNext.floor;
                    }
                } else if (externalNext.floor > elevator.getCurrentFloor()) {
                    nextFloor = externalNext.floor;
                    elevator.setDirection(externalNext.direction);
                }
                if (nextFloor == -1) {
                    elevator.setDirection(Direction.DOWN);
                    return findNextRequest(elevator);
                }
            } else if (elevator.getDirection() == Direction.DOWN) {
                if (internalNext == elevator.getCurrentFloor() || externalNext.floor == elevator.getCurrentFloor()) {
                    if (internalNext == elevator.getCurrentFloor()) {
                        internalRequests.poll();
                        return findNextRequest(elevator);
                    } else {
                        if (externalNext.direction == Direction.DOWN) {
                            externalRequests.poll();
                            return findNextRequest(elevator);
                        } else {
                            nextFloor = externalNext.floor;
                            elevator.setDirection(externalNext.direction);
                        }
                    }
                }
                if (internalNext < elevator.getCurrentFloor()) {
                    nextFloor = internalNext;
                    if (externalNext.floor < elevator.getCurrentFloor() && externalNext.floor > internalNext
                            && externalNext.direction == Direction.DOWN) {
                        nextFloor = externalNext.floor;
                    }
                } else if (externalNext.floor < elevator.getCurrentFloor()) {
                    nextFloor = externalNext.floor;
                    elevator.setDirection(externalNext.direction);
                }
                if (nextFloor == -1) {
                    elevator.setDirection(Direction.UP);
                    return findNextRequest(elevator);
                }
            }
        } else if (externalNext != null) {
            if (elevator.getDirection() == Direction.UP) {
                if (externalNext.floor == elevator.getCurrentFloor()) {
                    if (elevator.getDirection() == externalNext.direction) {
                        externalRequests.poll();
                        return findNextRequest(elevator);
                    } else {
                        nextFloor = externalNext.floor;
                        elevator.setDirection(externalNext.direction);
                    }
                }
                if (externalNext.floor > elevator.getCurrentFloor()) {
                    nextFloor = externalNext.floor;
                    elevator.setDirection(externalNext.direction);
                }
                if (nextFloor == -1) {
                    elevator.setDirection(Direction.DOWN);
                }
            } else if (elevator.getDirection() == Direction.DOWN) {
                if (externalNext.floor == elevator.getCurrentFloor()) {
                    if (elevator.getDirection() == externalNext.direction) {
                        externalRequests.poll();
                        return findNextRequest(elevator);
                    } else {
                        nextFloor = externalNext.floor;
                        elevator.setDirection(externalNext.direction);
                    }
                }
                if (externalNext.floor < elevator.getCurrentFloor()) {
                    nextFloor = externalNext.floor;
                    elevator.setDirection(externalNext.direction);
                }
                if (nextFloor == -1) {
                    elevator.setDirection(Direction.UP);
                }
            }
        } else if (internalNext != null) {
            if (elevator.getDirection() == Direction.UP) {
                if (internalNext == elevator.getCurrentFloor()) {
                    internalRequests.poll();
                    return findNextRequest(elevator);
                }
                if (internalNext > elevator.getCurrentFloor()) {
                    nextFloor = internalNext;
                }
                if (nextFloor == -1) {
                    elevator.setDirection(Direction.DOWN);
                }
            } else if (elevator.getDirection() == Direction.DOWN) {
                if (internalNext == elevator.getCurrentFloor()) {
                    internalRequests.poll();
                    return findNextRequest(elevator);
                }
                if (internalNext < elevator.getCurrentFloor()) {
                    nextFloor = internalNext;
                }
                if (nextFloor == -1) {
                    elevator.setDirection(Direction.UP);
                }
            }
        }
        return nextFloor;
    }

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

    public LinkedList<ExternalRequest> getExternalRequests() {
        return externalRequests;
    }
}

class Elevator {
    private int maxFloor;
    private int minFloor;
    private int currentFloor;
    private Direction direction;
    private RequestQueue requestQueue;
    private ElevatorStatus status;

    public Elevator(int maxFloor, int minFloor) {
        this.maxFloor = maxFloor;
        this.minFloor = minFloor;
        this.currentFloor = minFloor;
        this.direction = Direction.UP;
        this.requestQueue = new RequestQueue();
        this.status = ElevatorStatus.IDLE;
    }

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

    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 RequestQueue getRequestQueue() {
        return requestQueue;
    }

    public int getCurrentFloor() {
        return currentFloor;
    }

    public Direction getDirection() {
        return direction;
    }

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

    public void addInternalRequest(int floor) {
        if (isValidFloor(floor)) {
            requestQueue.addInternalRequest(floor);
            if (status == ElevatorStatus.IDLE) {
                status = ElevatorStatus.MOVING;
            }
        }
    }

    public void addExternalRequest(int floor, Direction direction) {
        if (isValidFloor(floor)) {
            requestQueue.addExternalRequest(floor, direction);
            if (status == ElevatorStatus.IDLE) {
                status = ElevatorStatus.MOVING;
            }
        }
    }

    public void processRequests() {
        int nextFloor = requestQueue.findNextRequest(this);
        if (nextFloor != -1) {
            status = ElevatorStatus.MOVING;
            if (currentFloor != nextFloor) {
                while (currentFloor != nextFloor) {
                    if (currentFloor < nextFloor) {
                        currentFloor++;
                        System.out.println("Current Floor: " + currentFloor + " Direction: UP");
                    } else {
                        currentFloor--;
                        System.out.println("Current Floor: " + currentFloor + " Direction: DOWN");
                    }
                }
            }
            System.out.println("Open Door # Floor " + currentFloor);
            System.out.println("Close Door");
            if (!requestQueue.getInternalRequests().isEmpty()
                    && requestQueue.getInternalRequests().peek() == nextFloor) {
                requestQueue.getInternalRequests().poll();
            }
            if (!requestQueue.getExternalRequests().isEmpty()
                    && requestQueue.getExternalRequests().peek().floor == nextFloor) {
                requestQueue.getExternalRequests().poll();
            }
            if (requestQueue.getInternalRequests().isEmpty() && requestQueue.getExternalRequests().isEmpty()) {
                status = ElevatorStatus.IDLE;
            } else {
                status = ElevatorStatus.MOVING;
            }
        }
    }

    public ElevatorStatus getStatus() {
        return status;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String data;
        int floor = 0;
        Direction direction = null;
        int minFloor, maxFloor;
        String request = "";
        ArrayList<String> list = new ArrayList<>();
        data = input.next();
        while (!data.equalsIgnoreCase("End")) {
            list.add(data);
            data = input.next();
        }
        minFloor = Integer.parseInt(list.get(0));
        maxFloor = Integer.parseInt(list.get(1));
        Elevator elevator = new Elevator(maxFloor, minFloor);
        for (int i = 2; i < list.size(); i++) {
            request = list.get(i);
            if (request.contains(",")) {
                if (!request.matches("<\\d+,\\s*(UP|DOWN)>")) {
                    System.out.println("Wrong Format");
                }
                String[] parts = request.replaceAll("[<>]", "").split(",");
                floor = Integer.parseInt(parts[0].trim());
                direction = Direction.valueOf(parts[1].trim().toUpperCase());
                elevator.addExternalRequest(floor, direction);
            } else {
                if (!request.matches("<\\d+>")) {
                    System.out.println("Wrong Format");
                }
                floor = Integer.parseInt(request.replaceAll("[<>]", ""));
                elevator.addInternalRequest(floor);
            }
        }
        System.out.println("Current Floor: " + elevator.getMinFloor() + " Direction: UP");
        while (!elevator.getRequestQueue().getInternalRequests().isEmpty()
                || !elevator.getRequestQueue().getExternalRequests().isEmpty()) {
            elevator.processRequests();
        }
        input.close();
    }
}    

(2)类间关系分析及类图

  • Main 类与 Elevator 类
    • 依赖关系:Main类通过创建Elevator类的实例来控制电梯行为。
  • Elevator 类与 RequestQueue 类
    • 包含关系:Elevator类中包含一个RequestQueue类型的成员变量requestQueue 。
  • Elevator 类与 ExternalRequest 类
    • 关联关系:当Elevator类调用RequestQueue类的addExternalRequest方法添加外部请求时,实际上是创建并添加了ExternalRequest类的实例到RequestQueue类的外部请求队列中。
  • RequestQueue 类与 ExternalRequest 类
    • 聚合关系:RequestQueue类中包含一个LinkedList<ExternalRequest>类型的成员变量externalRequests ,用于存储外部请求对象。RequestQueue类通过addExternalRequest方法创建ExternalRequest对象并添加到该列表中,同时提供getExternalRequests方法获取外部请求队列,表明RequestQueue类聚合了ExternalRequest类的实例,管理其生命周期。
  • 各类与枚举
    • 关联关系

(3)SourceMoniter分析代码:

 具体信息:

查看代码
  Metrics Details For File 'Main.java'
--------------------------------------------------------------------------------------------

Parameter				Value
=========				=====
Project Directory			C:\Users\25734\eclipse-workspace\Java\src\elevatorTest\
Project Name				代码分析2
Checkpoint Name				Baseline
File Name				Main.java
Lines					329
Statements				220
Percent Branch Statements		28.6
Method Call Statements			119
Percent Lines with Comments		0.0
Classes and Interfaces			6
Methods per Class			3.67
Average Statements per Method		8.09
Line Number of Most Complex Method	41
Name of Most Complex Method		RequestQueue.findNextRequest()
Maximum Complexity			53
Line Number of Deepest Block		53
Maximum Block Depth			7
Average Block Depth			3.29
Average Complexity			4.50

--------------------------------------------------------------------------------------------
Most Complex Methods in 4 Class(es):	Complexity, Statements, Max Depth, Calls

Elevator.addExternalRequest()		3, 4, 4, 2
Elevator.addInternalRequest()		3, 4, 4, 2
Elevator.Elevator()			1, 6, 2, 0
Elevator.getCurrentFloor()		1, 1, 2, 0
Elevator.getDirection()			1, 1, 2, 0
Elevator.getMaxFloor()			1, 1, 2, 0
Elevator.getMinFloor()			1, 1, 2, 0
Elevator.getRequestQueue()		1, 1, 2, 0
Elevator.getStatus()			1, 1, 2, 0
Elevator.isValidFloor()			2, 1, 2, 0
Elevator.processRequests()		13, 21, 6, 19
Elevator.setDirection()			1, 1, 2, 0
Elevator.setMaxFloor()			1, 1, 2, 0
Elevator.setMinFloor()			1, 1, 2, 0
ExternalRequest.ExternalRequest()	1, 2, 2, 0
Main.main()				9, 32, 5, 32
RequestQueue.addExternalRequest()	1, 1, 2, 1
RequestQueue.addInternalRequest()	1, 1, 2, 1
RequestQueue.findNextRequest()		53, 93, 7, 62
RequestQueue.getExternalRequests()	1, 1, 2, 0
RequestQueue.getInternalRequests()	1, 1, 2, 0
RequestQueue.RequestQueue()		1, 2, 2, 0

--------------------------------------------------------------------------------------------
Block Depth				Statements

0					10
1					32
2					51
3					24
4					35
5					38
6					22
7					8
8					0
9+					0
--------------------------------------------------------------------------------------------

  1.基本信息

  • 代码行数:329 行
  • 语句数:220 条

  2.代码结构相关

  • 类和接口数量:6 个(包括 Elevator 类、ExternalRequest 类、RequestQueue 类、Direction 枚举、ElevatorStatus 枚举、Main 类 )
  • 平均每个类的方法数:3.67

  3.复杂度相关

  • 平均复杂度:4.50
  • 最复杂方法:
  • 方法名:RequestQueue.findNextRequest()
  • 复杂度值:53 。此方法因需综合考虑电梯当前方向、内外部请求队列等多种情况,进行大量条件判断和逻辑处理来确定下一个请求楼层,致使复杂度较高。
  • Kiviat Graph(雷达图):呈现平均复杂度、平均深度、最大深度、最大复杂度、平均每个方法的语句数、注释比例等综合指标,可直观反映代码各方面复杂度特征。
  • Block Histogram(柱状图):横坐标为代码块深度,纵坐标为语句数。显示在代码块深度为 0 - 3 时语句数量较多,表明代码中相对浅层次的代码块包含了较多逻辑语句。

  体会:第二次迭代考虑了多类设计,将原本的Elevator类进行拆分,分别为Elevator类、RequestQueue类、其中电梯行为控制基本放在RequestQueue类当中,但是代码最高复杂度仍然有53,原因在于find NextRequest方法仍然采用的是第一次迭代的算法,基本未作修改。原因在于当时基于题目所给的类图不适用于我自己设计的电梯调度算法,在多次尝试修改无果后还是沿用了第一次的方案,但是在尝试的过程当中我还是认识到了单一职责的重要性,在这样的类设计之下,代码修改会变得更加方便快捷。

3、第三次迭代设计与分析

  (1)代码展示

查看代码

查看代码
  import java.util.LinkedList;
import java.util.Scanner;

// 乘客类
class Passenger {
    private int sourceFloor;
    private int destinationFloor;

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

    public int getSourceFloor() {
        return sourceFloor;
    }

    public int getDestinationFloor() {
        return destinationFloor;
    }

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

// 队列类
class RequestQueue {
    private LinkedList<Integer> internalRequests;
    private LinkedList<Passenger> externalRequests;

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

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

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

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

    public void addInternalRequest(Passenger passenger) {
        internalRequests.add(passenger.getDestinationFloor());
    }

    public void addExternalRequest(Passenger passenger) {
        externalRequests.add(passenger);
    }

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

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

// 电梯方向枚举
enum Direction {
    UP, DOWN
}

// 电梯状态枚举
enum ElevatorStatus {
    MOVING, STOP, IDLE
}

// 电梯类
class Elevator {
    private int maxFloor;
    private int minFloor;
    private int currentFloor;
    private Direction direction;
    private RequestQueue requestQueue;
    private ElevatorStatus status;

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

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

    public void setStatus(ElevatorStatus status) {
        this.status = status;
    }

    public Elevator(int maxFloor, int minFloor) {
        this.maxFloor = maxFloor;
        this.minFloor = minFloor;
        this.currentFloor = minFloor;
        this.direction = Direction.UP;
        this.requestQueue = new RequestQueue();
        this.status = ElevatorStatus.IDLE;
    }

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

    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 RequestQueue getRequestQueue() {
        return requestQueue;
    }

    public int getCurrentFloor() {
        return currentFloor;
    }

    public Direction getDirection() {
        return direction;
    }

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

    public void addInternalRequest(int floor) {
        if (isValidFloor(floor)) {
            requestQueue.addInternalRequest(floor);
            if (status == ElevatorStatus.IDLE) {
                status = ElevatorStatus.MOVING;
            }
        }
    }

    public void addInternalRequest(Passenger passenger) {
        if (isValidFloor(passenger.getDestinationFloor())) {
            requestQueue.addInternalRequest(passenger);
            if (status == ElevatorStatus.IDLE) {
                status = ElevatorStatus.MOVING;
            }
        }
    }

    public void addExternalRequest(Passenger passenger) {
        if (isValidFloor(passenger.getSourceFloor()) && isValidFloor(passenger.getDestinationFloor())) {
            requestQueue.addExternalRequest(passenger);
            if (status == ElevatorStatus.IDLE) {
                status = ElevatorStatus.MOVING;
            }
        }
    }

    public ElevatorStatus getStatus() {
        return status;
    }
}

// 控制类
class ElevatorController {
    private Elevator elevator;
    private RequestQueue 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 ElevatorController(Elevator elevator) {
        this.elevator = elevator;
        this.queue = elevator.getRequestQueue();
    }

    public int findNextRequest() {
        int nextFloor = -1;
        Integer internalNext = queue.getInternalRequests().isEmpty() ? null : queue.getInternalRequests().peek();
        Passenger externalNext = queue.getExternalRequests().isEmpty() ? null : queue.getExternalRequests().peek();

        if (externalNext != null && internalNext != null) {
            if (this.elevator.getDirection() == Direction.UP) {
                if (internalNext == this.elevator.getCurrentFloor() || externalNext.getSourceFloor() == this.elevator.getCurrentFloor()) {
                    if (internalNext == this.elevator.getCurrentFloor()) {
                        queue.getInternalRequests().poll();
                        return findNextRequest();
                    } else {
                        nextFloor = externalNext.getSourceFloor();
                    }
                }
                if (internalNext > this.elevator.getCurrentFloor()) {
                    nextFloor = internalNext;
                    if (externalNext.getSourceFloor() > this.elevator.getCurrentFloor() && externalNext.getSourceFloor() < internalNext) {
                        nextFloor = externalNext.getSourceFloor();
                    }
                } else if (externalNext.getSourceFloor() > this.elevator.getCurrentFloor()) {
                    nextFloor = externalNext.getSourceFloor();
                }
                if (nextFloor == -1) {
                    this.elevator.setDirection(Direction.DOWN);
                    return findNextRequest();
                }
            } else if (this.elevator.getDirection() == Direction.DOWN) {
                if (internalNext == this.elevator.getCurrentFloor() || externalNext.getSourceFloor() == this.elevator.getCurrentFloor()) {
                    if (internalNext == this.elevator.getCurrentFloor()) {
                        queue.getInternalRequests().poll();
                        return findNextRequest();
                    } else {
                        nextFloor = externalNext.getSourceFloor();
                    }
                }
                if (internalNext < this.elevator.getCurrentFloor()) {
                    nextFloor = internalNext;
                    if (externalNext.getSourceFloor() < this.elevator.getCurrentFloor() && externalNext.getSourceFloor() > internalNext) {
                        nextFloor = externalNext.getSourceFloor();
                    }
                } else if (externalNext.getSourceFloor() < this.elevator.getCurrentFloor()) {
                    nextFloor = externalNext.getSourceFloor();
                }
                if (nextFloor == -1) {
                    this.elevator.setDirection(Direction.UP);
                    return findNextRequest();
                }
            }
        } else if (externalNext != null) {
            if (this.elevator.getDirection() == Direction.UP) {
                if (externalNext.getSourceFloor() == this.elevator.getCurrentFloor()) {
                    nextFloor = externalNext.getSourceFloor();
                }
                if (externalNext.getSourceFloor() > this.elevator.getCurrentFloor()) {
                    nextFloor = externalNext.getSourceFloor();
                }
                if (nextFloor == -1) {
                    this.elevator.setDirection(Direction.DOWN);
                }
            } else if (this.elevator.getDirection() == Direction.DOWN) {
                if (externalNext.getSourceFloor() == this.elevator.getCurrentFloor()) {
                    nextFloor = externalNext.getSourceFloor();
                }
                if (externalNext.getSourceFloor() < this.elevator.getCurrentFloor()) {
                    nextFloor = externalNext.getSourceFloor();
                }
                if (nextFloor == -1) {
                    this.elevator.setDirection(Direction.UP);
                }
            }
        } else if (internalNext != null) {
            if (this.elevator.getDirection() == Direction.UP) {
                if (internalNext == this.elevator.getCurrentFloor()) {
                    queue.getInternalRequests().poll();
                    return findNextRequest();
                }
                if (internalNext > this.elevator.getCurrentFloor()) {
                    nextFloor = internalNext;
                }
                if (nextFloor == -1) {
                    this.elevator.setDirection(Direction.DOWN);
                }
            } else if (this.elevator.getDirection() == Direction.DOWN) {
                if (internalNext == this.elevator.getCurrentFloor()) {
                    queue.getInternalRequests().poll();
                    return findNextRequest();
                }
                if (internalNext < this.elevator.getCurrentFloor()) {
                    nextFloor = internalNext;
                }
                if (nextFloor == -1) {
                    this.elevator.setDirection(Direction.UP);
                }
            }
        }
        return nextFloor;
    }

    public void processRequests() {
        ElevatorStatus status = ElevatorStatus.IDLE;
        int currentFloor = this.elevator.getCurrentFloor();
        int nextFloor = findNextRequest();
        if (nextFloor != -1) {
            status = ElevatorStatus.MOVING;
            if (currentFloor != nextFloor) {
                while (currentFloor != nextFloor) {
                    if (currentFloor < nextFloor) {
                        currentFloor++;
                        System.out.println("Current Floor: " + currentFloor + " Direction: UP");
                    } else {
                        currentFloor--;
                        System.out.println("Current Floor: " + currentFloor + " Direction: DOWN");
                    }
                }
            }
            System.out.println("Open Door # Floor " + currentFloor);
            System.out.println("Close Door");

            RemoveRequest(currentFloor);

            if (queue.getInternalRequests().isEmpty() && queue.getExternalRequests().isEmpty()) {
                status = ElevatorStatus.IDLE;
            } else {
                status = ElevatorStatus.MOVING;
            }
            this.elevator.setCurrentFloor(currentFloor);
            this.elevator.setStatus(status);
        }
    }

    public void RemoveRequest(int currentFloor) {
        if (!queue.getExternalRequests().isEmpty() && queue.getExternalRequests().peek().getSourceFloor() == currentFloor) {
            Passenger passenger = queue.getExternalRequests().poll();
            queue.addInternalRequest(passenger);
        }

        if (!queue.getInternalRequests().isEmpty() && queue.getInternalRequests().peek().equals(currentFloor)) {
            queue.getInternalRequests().poll();
        }
    }

    public void handleInput(String input) {
        if (input.contains(",")) {
            String[] parts = input.replaceAll("[<>]", "").split(",");
            int sourceFloor = Integer.parseInt(parts[0].trim());
            int destinationFloor = Integer.parseInt(parts[1].trim());
            Passenger passenger = new Passenger(sourceFloor, destinationFloor);
            elevator.addExternalRequest(passenger);
        } else {
            int floor = Integer.parseInt(input.replaceAll("[<>]", ""));
            elevator.addInternalRequest(floor);
        }
    }

    public void runElevator() {
        System.out.println("Current Floor: " + elevator.getMinFloor() + " Direction: UP");
        while (!elevator.getRequestQueue().getInternalRequests().isEmpty()
                || !elevator.getRequestQueue().getExternalRequests().isEmpty()) {
            this.processRequests();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int minFloor = input.nextInt();
        int maxFloor = input.nextInt();
        input.nextLine(); // 消耗掉换行符
        Elevator elevator = new Elevator(maxFloor, minFloor);
        ElevatorController controller = new ElevatorController(elevator);

        String line;
        while (input.hasNextLine()) {
            line = input.nextLine().trim();
            if (line.equalsIgnoreCase("end")) {
                break;
            }
            if (!line.isEmpty()) {
                controller.handleInput(line);
            }
        }

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

(2)类间关系分析及类图

  • Main 类与 ElevatorController 类
    • 依赖关系:Main类通过创建ElevatorController类的实例来控制电梯运行。
  • ElevatorController 类与 Elevator 类
    • 关联关系:ElevatorController类持有Elevator类的实例,在构造函数中通过参数传入并将其赋值给成员变量 。ElevatorController类通过调用Elevator类的方法,如addInternalRequest 、addExternalRequest 、getCurrentFloor 、getRequestQueue 等,来控制电梯的行为和获取电梯的状态信息。
  • ElevatorController 类与 RequestQueue 类
    • 关联关系:ElevatorController类通过Elevator类获取RequestQueue类的实例 。在处理请求逻辑中,ElevatorController类调用RequestQueue类的方法,如getInternalRequests 、getExternalRequests 、addInternalRequest 、addExternalRequest 等,来管理内外部请求队列,确定下一个要处理的请求楼层(findNextRequest方法中对请求队列的操作 )以及在电梯到达楼层后移除已处理的请求(RemoveRequest方法 ) 。
  • Elevator 类与 Passenger 类
    • 关联关系:Elevator类通过Passenger类来获取参数。
  • RequestQueue 类与 Passenger 类
    • 聚合关系:RequestQueue类中包含一个LinkedList<Passenger>类型的成员变量externalRequests ,用于存储外部乘客请求对象。
  • 各类与枚举
    • 关联关系

(3)SourceMoniter分析代码:

 具体信息:

查看代码

查看代码
  Metrics Details For File 'Main.java'
--------------------------------------------------------------------------------------------

Parameter				Value
=========				=====
Project Directory			C:\Users\25734\eclipse-workspace\Java\src\elevatorTest\
Project Name				代码分析3
Checkpoint Name				Baseline
File Name				Main.java
Lines					392
Statements				232
Percent Branch Statements		25.0
Method Call Statements			134
Percent Lines with Comments		1.8
Classes and Interfaces			2
Methods per Class			20.00
Average Statements per Method		5.65
Line Number of Most Complex Method	187
Name of Most Complex Method		Main.main()
Maximum Complexity			4
Line Number of Deepest Block		209
Maximum Block Depth			5
Average Block Depth			1.80
Average Complexity			4.00

--------------------------------------------------------------------------------------------
Most Complex Methods in 1 Class(es):	Complexity, Statements, Max Depth, Calls

Main.main()				4, 13, 4, 10

--------------------------------------------------------------------------------------------
Block Depth				Statements

0					56
1					61
2					43
3					29
4					31
5					12
6					0
7					0
8					0
9+					0
--------------------------------------------------------------------------------------------

 1.基本信息

  • 代码行数: 392 行
  • 语句数:232 条

 2.代码结构相关

  • 平均每个类的方法数:平均每个类的方法数为 20.00 ,说明类中定义了较多方法,代码功能相对细化。

  3.复杂度相关

  • 平均复杂度:平均复杂度为 4.00 ,整体复杂度处于一定水平。
  • 最复杂方法:最复杂的方法是Main.main() ,复杂度(Maximum Complexity )为 4 ,位于第 187 行 。Main.main()方法主要负责获取用户输入、初始化电梯和控制器并进行请求处理的循环。

体会:第三次迭代是基于第二次迭代的简单迭代,没有像之前第一次向第二次迭代那样大刀阔斧,只是添加了一个Passenger类,将原来Main类中的用户输入另开一个类以达到单一职责的目的。这次的电梯算法基于第二次有所修改,外部请求也由原来的方向加楼层改为了楼层与楼层,对请求的处理有了更高的要求。在测试过程中,我始终有一个测试点难以过去,在多次尝试后也没有成功。但是在尝试的过程中,锻炼了自己的耐心,虽然没有拿到满分,但是还是有所收获。

三、踩坑心得

  • 第一次迭代中的输入格式:在完成所有的编写工作后我尝试提交,发现答案错误(因为测试用例上的楼层及开关门都能对上,所以以为自己可以通过测试,结果一串的答案错误给自己浇了一盆冷水)。当时实在没有想明白为什么,苦心钻研是否是算法存在错误(一个晚自习无果后选择放弃),第二天发现只是输出形式存在问题:原输出形式(System.out.println("Current Floor: " + elevator.getMinFloor() + " Direction: UP");)即到达目的楼层后其电梯方向为电梯到下一楼层的方向而非电梯的运行方向。而后改为if (currentFloor < nextFloor) {
                            currentFloor++;
                            System.out.println("Current Floor: " + currentFloor + " Direction: UP");
                        } else {
                            currentFloor--;
                            System.out.println("Current Floor: " + currentFloor + " Direction: DOWN");
                        }的形式后提交,通过了所有的测试点。经过这个问题,我发现冷静思考有多么重要,在长时间的思考中没有头绪我们常常会感到厌烦,对问题的理解就不够具体,所谓将问题复杂化就是思考不够细致,不够全面。
  • 算法设计:在老师没有给出测试用例及Main方法前,感觉自己就像个无头苍蝇,没有头绪。在经过几天的无用功后,还是放弃了这道题。后来重新加入题目集后,我重新审题,按照老师对我们的提点去一步一步分析电梯的运行逻辑,而后我尝试手绘算法,最后也是成功通过了测试。

  • 非零返回:

    第二次迭代在运行测试阶段都能做出正确输出,但是在提交过程中始终得到非零返回,原因在于LinkedList中的peek(取出头结点)方法在头结点为空时返回非零值,但是由于缺乏对这个方法的了解,导致一直卡壳。后面查阅资料才有所发现。并在findNextRequest方法中添加了当内外部请求分别为空的条件处理。

  • 外部请求处理:在第三次迭代处理外部请求的时候,最初我认为是一次性处理所有请求,仅简单将外部请求的目标楼层加到内部请求尾部,导致最终答案与正确输出完全不匹配。然后开始修改算法,这个过程也耗费了大量时间。

四、改进建议

  • 代码精简 :
    查看代码
          public int findNextRequest() {
            int nextFloor = -1;
            Integer internalNext = queue.getInternalRequests().isEmpty() ? null : queue.getInternalRequests().peek();
            Passenger externalNext = queue.getExternalRequests().isEmpty() ? null : queue.getExternalRequests().peek();
    
            if (externalNext != null && internalNext != null) {
                if (this.elevator.getDirection() == Direction.UP) {
                    if (internalNext == this.elevator.getCurrentFloor() || externalNext.getSourceFloor() == this.elevator.getCurrentFloor()) {
                        if (internalNext == this.elevator.getCurrentFloor()) {
                            queue.getInternalRequests().poll();
                            return findNextRequest();
                        } else {
                            nextFloor = externalNext.getSourceFloor();
                        }
                    }
                    if (internalNext > this.elevator.getCurrentFloor()) {
                        nextFloor = internalNext;
                        if (externalNext.getSourceFloor() > this.elevator.getCurrentFloor() && externalNext.getSourceFloor() < internalNext) {
                            nextFloor = externalNext.getSourceFloor();
                        }
                    } else if (externalNext.getSourceFloor() > this.elevator.getCurrentFloor()) {
                        nextFloor = externalNext.getSourceFloor();
                    }
                    if (nextFloor == -1) {
                        this.elevator.setDirection(Direction.DOWN);
                        return findNextRequest();
                    }
                } else if (this.elevator.getDirection() == Direction.DOWN) {
                    if (internalNext == this.elevator.getCurrentFloor() || externalNext.getSourceFloor() == this.elevator.getCurrentFloor()) {
                        if (internalNext == this.elevator.getCurrentFloor()) {
                            queue.getInternalRequests().poll();
                            return findNextRequest();
                        } else {
                            nextFloor = externalNext.getSourceFloor();
                        }
                    }
                    if (internalNext < this.elevator.getCurrentFloor()) {
                        nextFloor = internalNext;
                        if (externalNext.getSourceFloor() < this.elevator.getCurrentFloor() && externalNext.getSourceFloor() > internalNext) {
                            nextFloor = externalNext.getSourceFloor();
                        }
                    } else if (externalNext.getSourceFloor() < this.elevator.getCurrentFloor()) {
                        nextFloor = externalNext.getSourceFloor();
                    }
                    if (nextFloor == -1) {
                        this.elevator.setDirection(Direction.UP);
                        return findNextRequest();
                    }
                }
            } else if (externalNext != null) {
                if (this.elevator.getDirection() == Direction.UP) {
                    if (externalNext.getSourceFloor() == this.elevator.getCurrentFloor()) {
                        nextFloor = externalNext.getSourceFloor();
                    }
                    if (externalNext.getSourceFloor() > this.elevator.getCurrentFloor()) {
                        nextFloor = externalNext.getSourceFloor();
                    }
                    if (nextFloor == -1) {
                        this.elevator.setDirection(Direction.DOWN);
                    }
                } else if (this.elevator.getDirection() == Direction.DOWN) {
                    if (externalNext.getSourceFloor() == this.elevator.getCurrentFloor()) {
                        nextFloor = externalNext.getSourceFloor();
                    }
                    if (externalNext.getSourceFloor() < this.elevator.getCurrentFloor()) {
                        nextFloor = externalNext.getSourceFloor();
                    }
                    if (nextFloor == -1) {
                        this.elevator.setDirection(Direction.UP);
                    }
                }
            } else if (internalNext != null) {
                if (this.elevator.getDirection() == Direction.UP) {
                    if (internalNext == this.elevator.getCurrentFloor()) {
                        queue.getInternalRequests().poll();
                        return findNextRequest();
                    }
                    if (internalNext > this.elevator.getCurrentFloor()) {
                        nextFloor = internalNext;
                    }
                    if (nextFloor == -1) {
                        this.elevator.setDirection(Direction.DOWN);
                    }
                } else if (this.elevator.getDirection() == Direction.DOWN) {
                    if (internalNext == this.elevator.getCurrentFloor()) {
                        queue.getInternalRequests().poll();
                        return findNextRequest();
                    }
                    if (internalNext < this.elevator.getCurrentFloor()) {
                        nextFloor = internalNext;
                    }
                    if (nextFloor == -1) {
                        this.elevator.setDirection(Direction.UP);
                    }
                }
            }
            return nextFloor;
        }
    findNextRequest方法代码超过90行,且方向上行与方向下行的查找逻辑基本一致,说明可以对代码进行简化,将方向上下行分开成两部分也可以是阅读、修改更顺利。
  • 功能细化:第三次迭代类图中将Controller类划分成许多方法,而我的代码将其中的determineDirection()、shouldStop()、getClosest()、move()方法全都一股脑放在了processRequest方法当中,未完全做到职责单一原则。后续有时间我也会将代码再进行细分处理。

五、总结

    收获及建议:

收获:

  经过这将近一个月的迭代训练,我的Java编程能力确实得到了提升。但更重要的是编程思维的提升,老师给的迭代作业中的算法设计虽然前期确实比较得耗费时间去理解、复现。但是在完成的那一刻,还是给了我相当大的鼓舞。可以说,没有哪一次作业能让我获得如此大的成就感。即使我没有在第一时间拿下题目集(也就是没有拿到分),但是后续的思考过程才是我真真正正获得到的提升。通过这三次迭代训练,我掌握了LinkedList以及相关方法的使用,对Look算法有了基本的认识,更对单一职责原则(SPR)有了深刻的理解(以前总是对其不以为意,当代码量提高以后,在代码修改过程中老实了)。

建议:

  经过这三次的迭代的训练,我也希望向面向对象课程组的老师提一些建议:在迭代过程中,我们常常遇到一些基础知识的漏洞,虽然老师平常常强调自主学习,但是有些易错点还是希望老师上课可以提及一些。题目设计方面:希望老师可以多给一些测试点,让我们可以在测试过程当中找出自己的错误。因为PTA的得分就是依靠测试点,与其让学生一直死磕没有头绪,不如将学生从死循环当中解救出来,给一个方向(提示)再让我们更好地利用时间。

posted @ 2025-04-20 19:35  肖文浩  阅读(11)  评论(0)    收藏  举报