第一次blog作业 Java电梯分析,从c语言到java,
第一次blog作业 Java电梯分析,从c语言到java,
一、前言
作为第一次Java大作业,对我自己而言有着非常大的挑战性。从c语言到java,在Java的学习中我已经开始由面向过程的思维转向面向对象的思维。但由于自己设计经验欠缺及语法知识的不足, 在这第一轮
大作业中遇到了许多的困难。虽然取得的成绩很不理想,但在向他人请教与自己学习的艰苦过程中我确实学习到了很多。如ArrayList的运用,enum枚举等方法的运用。下面我就将详细展现我的三次电梯程
序的编写过程,遇到的问题,并利用SourceMonitor的生成报表内容以及PowerDesigner的相应类图来分析我的程序。
二、设计与分析
1,第一次单部电梯调度程序
题目:
作为第一次Java大作业,对我自己而言有着非常大的挑战性。从c语言到java,在Java的学习中我已经开始由面向过程的思维转向面向对象的思维。但由于自己设计经验欠缺及语法知识的不足, 在这第一轮
大作业中遇到了许多的困难。虽然取得的成绩很不理想,但在向他人请教与自己学习的艰苦过程中我确实学习到了很多。如ArrayList的运用,enum枚举等方法的运用。下面我就将详细展现我的三次电梯程
序的编写过程,遇到的问题,并利用SourceMonitor的生成报表内容以及PowerDesigner的相应类图来分析我的程序。
1,第一次单部电梯调度程序
题目:
设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。
请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求),具体运行规则详见输入输出样例。
输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:
运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
感想与遇到的问题:
在看到这个题目后我大为害怕,前四道题目对那时的我已经比较有难度,所以这题我并没有在可以得分的情况下做出。但在自己不断的调试以及朋友的帮组下。我终于是在后面成功有了自己成功的代码。在不断的调试过程中,如何让按要求让电梯处理内部及外部请求等难点给我留下了深刻的映像。电梯运行状态包括停止、移动中、开门、关门等,不同状态下对请求的处理方式不同。在程序设计初期,我没有合理规划状态之间的转换逻辑,导致电梯在某些情况下出现异常行为。比如,电梯在开门状态下还在继续移动,这显然不符合实际情况。经过反复思考,我为电梯类添加了状态变量,并在不同的操作方法中加入了状态判断,确保电梯在正确的状态下执行相应操作。还有由于程序涉及多个类和复杂的逻辑,在调试过程中很难定位问题所在。尤其是在处理请求队列和电梯运行方向判断时,程序的实际运行结果与预期结果相差较大。我通过在关键代码处添加大量的输出语句,打印变量值和程序执行流程信息,逐步排查问题。同时,借助 IDE 的调试工具,单步执行代码,观察变量变化,最终找到了问题根源。
源码:
点击查看代码
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
// 定义电梯运行方向的枚举类
enum LiftDirection {
UPWARD, DOWNWARD
}
// 外部请求类,存储外部乘客请求信息
class OutsideRequest {
private int floorNumber;
private LiftDirection travelDirection;
public OutsideRequest(int floor, LiftDirection direction) {
this.floorNumber = floor;
this.travelDirection = direction;
}
public int getFloor() {
return floorNumber;
}
public LiftDirection getDirection() {
return travelDirection;
}
}
// 电梯类,封装电梯属性和操作方法
class Lift {
private int lowestFloor;
private int highestFloor;
private int currentPosition;
private LiftDirection currentMovingDirection = LiftDirection.UPWARD;
private List<Integer> insideRequests = new ArrayList<>();
private List<OutsideRequest> outsideRequests = new ArrayList<>();
public Lift(int minFloor, int maxFloor) {
this.lowestFloor = minFloor;
this.highestFloor = maxFloor;
this.currentPosition = minFloor;
}
// 添加内部请求
public void addInsideRequest(int floor) {
insideRequests.add(floor);
}
// 添加外部请求
public void addOutsideRequest(int floor, LiftDirection direction) {
outsideRequests.add(new OutsideRequest(floor, direction));
}
// 获取内部请求队列
public List<Integer> getInsideRequests() {
return insideRequests;
}
// 获取外部请求队列
public List<OutsideRequest> getOutsideRequests() {
return outsideRequests;
}
// 处理请求
public void handleRequests() {
System.out.println("Current Floor: " + currentPosition + " Direction: " + currentMovingDirection);
while (!insideRequests.isEmpty() || !outsideRequests.isEmpty()) {
moveLift();
}
}
// 确定电梯运行方向
private void decideDirection() {
int target = -1;
if (!insideRequests.isEmpty()) {
target = insideRequests.get(0);
} else if (!outsideRequests.isEmpty()) {
target = outsideRequests.get(0).getFloor();
}
if (target != -1) {
if (target < currentPosition) {
currentMovingDirection = LiftDirection.DOWNWARD;
} else if (target > currentPosition) {
currentMovingDirection = LiftDirection.UPWARD;
}
}
}
// 电梯移动
private void moveLift() {
if (currentMovingDirection == LiftDirection.UPWARD) {
currentPosition++;
} else if (currentMovingDirection == LiftDirection.DOWNWARD) {
currentPosition--;
}
System.out.println("Current Floor: " + currentPosition + " Direction: " + currentMovingDirection);
if (shouldStopHere()) {
System.out.println("Open Door # Floor " + currentPosition);
System.out.println("Close Door");
}
decideDirection();
}
// 判断是否应该在当前楼层停止
private boolean shouldStopHere() {
return processInsideRequests() || processOutsideRequests();
}
// 处理内部请求
private boolean processInsideRequests() {
if (!insideRequests.isEmpty() && insideRequests.get(0) == currentPosition) {
insideRequests.remove(0);
return true;
}
return false;
}
// 处理外部请求
private boolean processOutsideRequests() {
if (!outsideRequests.isEmpty() && outsideRequests.get(0).getFloor() == currentPosition) {
if (!insideRequests.isEmpty()) {
OutsideRequest firstReq = outsideRequests.get(0);
if (firstReq.getDirection() == currentMovingDirection
|| (firstReq.getDirection() == LiftDirection.UPWARD && insideRequests.get(0) > firstReq.getFloor())
|| (firstReq.getDirection() == LiftDirection.DOWNWARD && insideRequests.get(0) < firstReq.getFloor())) {
int firstFloor = firstReq.getFloor();
int idx = 0;
while (idx < outsideRequests.size() && outsideRequests.get(idx).getFloor() == firstFloor) {
idx++;
}
currentMovingDirection = firstReq.getDirection();
outsideRequests.subList(0, idx).clear();
return true;
}
} else {
int firstFloor = outsideRequests.get(0).getFloor();
int idx = 0;
while (idx < outsideRequests.size() && outsideRequests.get(idx).getFloor() == firstFloor) {
idx++;
}
outsideRequests.subList(0, idx).clear();
for (int i = 1; i < idx; i++) {
System.out.println("Open Door # Floor " + currentPosition);
System.out.println("Close Door");
}
return true;
}
}
return false;
}
}
// 主类,程序入口
public class Main {
public static void main(String[] args) {
Scanner inputScanner = new Scanner(System.in);
List<String> inputData = new ArrayList<>();
String inputLine;
while (!(inputLine = inputScanner.next()).equalsIgnoreCase("End")) {
inputData.add(inputLine);
}
int minFloor = Integer.parseInt(inputData.get(0));
int maxFloor = Integer.parseInt(inputData.get(1));
Lift elevator = new Lift(minFloor, maxFloor);
for (int i = 2; i < inputData.size(); i++) {
String request = inputData.get(i);
if (request.contains(",")) {
if (!request.matches("<\\d+,\\s*(UPWARD|DOWNWARD)>")) {
System.out.println("Wrong Format");
} else {
String[] parts = request.replaceAll("[<>]", "").split(",");
int floor = Integer.parseInt(parts[0].trim());
LiftDirection direction = LiftDirection.valueOf(parts[1].trim().toUpperCase());
elevator.addOutsideRequest(floor, direction);
}
} else {
if (!request.matches("<\\d+>")) {
System.out.println("Wrong Format");
} else {
int floor = Integer.parseInt(request.replaceAll("[<>]", ""));
elevator.addInsideRequest(floor);
}
}
}
elevator.handleRequests();
inputScanner.close();
}
}
类图:
代码分析:
分析
代码基本信息:项目目录可能存在编码问题,显示的字符异常。项目名称为 666 ,文件 666.txt 。代码总行数 196 行,语句数 114 条,分支语句占比 21.9%,方法调用语句 71 条。
代码结构:有 3 个类和接口,平均每个类的方法数为 4.67 个,每个方法平均语句数 6.21 条。整体结构有一定复杂度。
复杂度情况:最复杂方法为 Lift.decideDirection() ,复杂度达 6,行号 73 。最深代码块在 131 行。
注释情况:含注释的行占比仅 7.1%,注释过少。
图表信息:雷达图展示了多个维度指标,柱状图显示了不同深度代码块中语句的分布,深度 1 和 2 中语句较多。
问题
项目目录显示异常字符,是否存在编码错误,会否影响项目运行和文件读取?
注释占比如此之低,新接手代码的人员能否快速理解代码逻辑?
Lift.decideDirection() 方法复杂度较高,是否会导致调试困难,影响代码可读性和可维护性?
改进地方
解决编码问题:排查项目目录的编码,确保项目正常运行和文件操作无误。
增加注释:对关键方法、逻辑复杂处添加注释,提高代码可读性和可维护性。
优化复杂方法:对 Lift.decideDirection() 等复杂方法进行重构,拆分逻辑,降低复杂度。
平衡代码块深度:参考柱状图,审视深度较大的代码块,尝试优化,避免过深嵌套。
2,第二次单部电梯调度程序
题目:
对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客请求类、队列类以及控制类,具体设计可参考如下类图。
电梯迭代1类图.png
电梯运行规则与前阶段单类设计相同,但要处理如下情况:
乘客请求楼层数有误,具体为高于最高楼层数或低于最低楼层数,处理方法:程序自动忽略此类输入,继续执行
乘客请求不合理,具体为输入时出现连续的相同请求,例如<3><3><3>或者<5,DOWN><5,DOWN>,处理方法:程序自动忽略相同的多余输入,继续执行,例如<3><3><3>过滤为<3>
注意:本次作业类设计必须符合如上要求(包含但不限于乘客请求类、电梯类、请求队列类及控制类,其中控制类专门负责电梯调度过程),凡是不符合类设计要求此题不得分,另外,PTA得分代码界定为第一次提交的最高分代码(因此千万不要把第一次电梯程序提交到本次题目中测试)。
输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:
运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
感想与遇到的问题:
在着手设计类的过程中,如何精准地将电梯类、乘客请求类、队列类以及控制类的职责区分开来,成了一道棘手的难题。我不断地看这题目给出的类图,思考怎样的设计才能既满足题目要求,又能让代码结构清晰、易于维护。好在这次是在上一次题目修改后得到的,但由于上一次题目我花费时间太多,这一次我也并没有拿到分数(哭)。在控制类 Controller 中,确定电梯运行方向的逻辑 determineDirection 方法,我很难准确判断什么时候该设置为上行,什么时候该设置为下行。尤其是当内部请求队列和外部请求队列都有请求时,要综合考虑当前楼层、请求楼层以及请求方向来确定电梯运行方向,我调试了很多次才让它基本符合电梯运行规则。还有在处理停层逻辑 checkStop 方法里,处理外部请求和内部请求的先后顺序以及判断是否该停层的条件,也让我头疼了好久,总是会出现不该停的时候停了,或者该停的时候没停的情况,最终得到的代码只过了一个测试点。
源码:
点击查看代码
import java.util.LinkedList;
import java.util.Scanner;
// 定义电梯运行方向的枚举类
enum Direction {
UP, DOWN
}
// 外部请求类
class ExternalRequest {
private int floor;
private Direction direction;
// 构造方法
public ExternalRequest(int floor, Direction direction) {
this.floor = floor;
this.direction = direction;
}
// 获取请求楼层
public int getFloor() {
return floor;
}
// 获取请求方向
public Direction getDirection() {
return direction;
}
}
// 队列管理类
class RequestQueue {
private LinkedList<Integer> internalRequests;
private LinkedList<ExternalRequest> externalRequests;
private int minFloor;
private int maxFloor;
// 初始化队列
public RequestQueue(int minFloor, int maxFloor) {
this.minFloor = minFloor;
this.maxFloor = maxFloor;
internalRequests = new LinkedList<>();
externalRequests = new LinkedList<>();
}
// 添加内部请求(过滤非法和重复)
public void addInternalRequest(int floor) {
if (floor < minFloor || floor > maxFloor) {
return;
}
if (!internalRequests.contains(floor)) {
internalRequests.add(floor);
}
}
// 添加外部请求(过滤非法和重复)
public void addExternalRequest(int floor, Direction direction) {
if (floor < minFloor || floor > maxFloor) {
return;
}
ExternalRequest newRequest = new ExternalRequest(floor, direction);
if (!externalRequests.contains(newRequest)) {
externalRequests.add(newRequest);
}
}
// 获取内部请求队列
public LinkedList<Integer> getInternalRequests() {
return internalRequests;
}
// 获取外部请求队列
public LinkedList<ExternalRequest> getExternalRequests() {
return externalRequests;
}
}
// 电梯状态管理类
class Elevator {
private int minFloor;
private int maxFloor;
private int currentFloor;
private Direction currentDirection;
// 初始化电梯状态
public Elevator(int minFloor, int maxFloor) {
this.minFloor = minFloor;
this.maxFloor = maxFloor;
currentFloor = minFloor;
currentDirection = Direction.UP;
}
// 获取当前楼层
public int getCurrentFloor() {
return currentFloor;
}
// 获取当前方向
public Direction getCurrentDirection() {
return currentDirection;
}
// 电梯移动
public void move() {
if (currentDirection == Direction.UP) {
currentFloor++;
} else {
currentFloor--;
}
System.out.println("Current Floor: " + currentFloor + " Direction: " + currentDirection);
}
// 设置运行方向
public void setCurrentDirection(Direction direction) {
currentDirection = direction;
}
// 电梯开门
public void openDoor() {
System.out.println("Open Door # Floor " + currentFloor);
}
// 电梯关门
public void closeDoor() {
System.out.println("Close Door");
}
}
// 电梯控制类
class Controller {
private Elevator elevator;
private RequestQueue queue;
// 初始化控制类
public Controller(Elevator elevator, RequestQueue queue) {
this.elevator = elevator;
this.queue = queue;
}
// 设置电梯运行方向为上行
private void setToUp() {
elevator.setCurrentDirection(Direction.UP);
}
// 设置电梯运行方向为下行
private void setToDown() {
elevator.setCurrentDirection(Direction.DOWN);
}
// 处理请求主循环
public void processRequests() {
System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getCurrentDirection());
while (!queue.getInternalRequests().isEmpty() || !queue.getExternalRequests().isEmpty()) {
determineDirection();
elevator.move();
checkStop();
}
}
// 确定运行方向
private void determineDirection() {
LinkedList<Integer> inReqs = queue.getInternalRequests();
LinkedList<ExternalRequest> exReqs = queue.getExternalRequests();
int current = elevator.getCurrentFloor();
int targetFloor = -1;
if (!inReqs.isEmpty()) {
targetFloor = inReqs.getFirst();
} else if (!exReqs.isEmpty()) {
targetFloor = exReqs.getFirst().getFloor();
}
if (targetFloor != -1) {
if (targetFloor > current) {
setToUp();
} else if (targetFloor < current) {
setToDown();
}
}
}
// 检查停层
private void checkStop() {
int currentFloor = elevator.getCurrentFloor();
boolean hasStopped = handleExternalRequests(currentFloor);
if (!hasStopped) {
handleInternalRequests(currentFloor);
}
}
private boolean handleExternalRequests(int currentFloor) {
LinkedList<ExternalRequest> exReqs = queue.getExternalRequests();
if (!exReqs.isEmpty()) {
ExternalRequest firstReq = exReqs.getFirst();
if (firstReq.getFloor() == currentFloor) {
if (firstReq.getDirection() == elevator.getCurrentDirection()) {
int index = 0;
while (index < exReqs.size() && exReqs.get(index).getFloor() == currentFloor) {
index++;
}
exReqs.subList(0, index).clear();
for (int i = 0; i < index; i++) {
elevator.openDoor();
elevator.closeDoor();
}
return true;
} else {
LinkedList<Integer> inReqs = queue.getInternalRequests();
if (!inReqs.isEmpty()) {
int internalTarget = inReqs.getFirst();
if ((firstReq.getDirection() == Direction.UP && internalTarget > currentFloor)
|| (firstReq.getDirection() == Direction.DOWN && internalTarget < currentFloor)) {
exReqs.removeFirst();
elevator.openDoor();
elevator.closeDoor();
if (firstReq.getDirection() == Direction.UP) {
setToUp();
} else {
setToDown();
}
return true;
}
}
}
}
}
return false;
}
private void handleInternalRequests(int currentFloor) {
LinkedList<Integer> inReqs = queue.getInternalRequests();
if (!inReqs.isEmpty() && inReqs.getFirst() == currentFloor) {
inReqs.removeFirst();
elevator.openDoor();
elevator.closeDoor();
}
}
}
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();
RequestQueue queue = new RequestQueue(minFloor, maxFloor);
Elevator elevator = new Elevator(minFloor, maxFloor);
Controller controller = new Controller(elevator, queue);
String input;
while (!(input = scanner.nextLine().trim()).equalsIgnoreCase("end")) {
if (input.contains(",")) {
String[] parts = input.replaceAll("[<>]", "").split(",");
int floor = Integer.parseInt(parts[0].trim());
Direction direction = Direction.valueOf(parts[1].trim().toUpperCase());
queue.addExternalRequest(floor, direction);
} else {
int floor = Integer.parseInt(input.replaceAll("[<>]", ""));
queue.addInternalRequest(floor);
}
}
controller.processRequests();
scanner.close();
}
}
类图:
代码分析:
代码分析
代码规模:文件有 266 行,包含 141 条语句,其中分支语句占比 19.1% ,方法调用语句 75 条。从这些数据看,代码有一定规模,语句分布反映出逻辑判断和方法调用较为常见。
类与方法:有 3 个类和接口 ,平均每个类有 4.67 个方法,平均每个方法有 9.5 条语句 。说明代码有一定的模块化,但方法内语句数较多,可能存在方法功能不够单一的情况。
复杂度:最复杂方法是 Main.main() ,复杂度为 4 ,深度最深的代码块在 204 行 。表明 main 方法逻辑相对复杂,可能耦合了较多功能。
注释情况:带注释的行数占比仅 9.8% ,注释过少,不利于代码的理解和维护。
图表:Kiviat 图显示各项指标分布情况,柱状图展示了语句深度分布,能直观反映代码结构特征。
提出问题
可维护性问题:注释占比如此之低,后续阅读和修改代码时,很难快速理解代码逻辑,尤其是当新的开发人员接手时,理解成本会很高。
方法复杂度问题:Main.main() 方法复杂度最高,意味着该方法可能承担了过多的职责,包含了多种不同类型的逻辑处理,这不符合代码设计的单一职责原则,不利于代码的测试和维护。
类设计合理性问题:虽然有一定数量的类和方法,但平均每个方法语句数较多,是否存在类的职责划分不够清晰的情况,导致一些方法承担了过多的功能?
改进建议
增加注释:对关键方法、复杂逻辑以及变量的作用添加注释,提高代码可读性。比如在每个类的开头说明类的职责,在方法开头解释方法的功能、参数含义和返回值意义。
重构复杂方法:针对 Main.main() 等复杂方法,分析其功能,将不同功能拆分成独立的小方法,遵循单一职责原则。例如,如果 main 方法中同时包含输入处理、对象初始化和业务逻辑处理,可将这些功能分别提取到单独方法中。
优化类设计:重新审视类与方法的职责划分,检查是否有方法可以进一步拆分到更合适的类中,或者是否存在可以提取公共逻辑到新类或工具类中的情况,使代码结构更加清晰合理。
3,第三次单部电梯调度程序
题目:
对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客类、队列类以及控制类,具体设计可参考如下类图。
类图.png
电梯运行规则与前阶段相同,但有如下变动情况:
乘客请求输入变动情况:外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)
注意:本次作业类设计必须符合如上要求(包含但不限于设计电梯类、乘客类、队列类以及控制类),凡是不符合类设计要求此题不得分,另外,PTA得分代码界定为第一次提交的最高分代码(因此千万不要把第一次及第二次电梯程序提交到本次题目中测试)。
输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。
电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<请求源楼层,请求目的楼层>,其中,请求源楼层表示乘客发起请求所在的楼层,请求目的楼层表示乘客想要到达的楼层。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:
运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
感想与遇到的问题:
第三次的题目依然是在第二次的基础上进行编写,但难度却有上了一个台阶,在pta上我一直遇到显示多种错误,这让我很难绷(苦)如对于非法楼层请求(高于最高楼层或低于最低楼层)和重复请求要进行过滤。判断非法请求相对容易些,通过比较楼层数和电梯的最大、最小楼层即可。但处理重复请求时,我需要在队列中判断是否已经存在相同的请求,对于外部请求还要考虑源楼层和目的楼层都相同的情况,在数据结构(LinkedList)上实现这个判断逻辑时,我调试了多次才确保准确过滤重复请求。
源码:
点击查看代码
import java.util.LinkedList;
import java.util.Scanner;
// 定义电梯运行方向的枚举类
enum Direction {
UP, DOWN
}
// 定义电梯状态的枚举类
enum State {
MOVING, STOPPED
}
// 乘客类
class Passenger {
private Integer sourceFloor;
private Integer destinationFloor;
public Passenger(Integer sourceFloor, Integer destinationFloor) {
this.sourceFloor = sourceFloor;
this.destinationFloor = destinationFloor;
}
public Passenger(Integer destinationFloor) {
this.destinationFloor = destinationFloor;
}
public Integer getSourceFloor() {
return sourceFloor;
}
public void setSourceFloor(Integer sourceFloor) {
this.sourceFloor = sourceFloor;
}
public Integer getDestinationFloor() {
return destinationFloor;
}
public void setDestinationFloor(Integer destinationFloor) {
this.destinationFloor = destinationFloor;
}
public Direction getDirection() {
if (sourceFloor != null && destinationFloor != null) {
return destinationFloor > sourceFloor? Direction.UP : Direction.DOWN;
}
return null;
}
}
// 队列类
class RequestQueue {
private LinkedList<Passenger> internalRequests;
private LinkedList<Passenger> externalRequests;
public RequestQueue() {
internalRequests = new LinkedList<>();
externalRequests = new LinkedList<>();
}
public RequestQueue getQueueInstance() {
return this;
}
public LinkedList<Passenger> getInternalRequests() {
return internalRequests;
}
public void setInternalRequests(LinkedList<Passenger> internalRequests) {
this.internalRequests = internalRequests;
}
public LinkedList<Passenger> getExternalRequests() {
return externalRequests;
}
public void setExternalRequests(LinkedList<Passenger> externalRequests) {
this.externalRequests = externalRequests;
}
public void addInternalRequest(Passenger passenger) {
internalRequests.add(passenger);
}
public void addExternalRequest(Passenger passenger) {
externalRequests.add(passenger);
}
}
// 电梯类
class Elevator {
private int currentFloor;
private Direction direction;
private State state;
private int maxFloor;
private int minFloor;
public Elevator(int minFloor, int maxFloor) {
this.currentFloor = minFloor;
this.direction = Direction.UP;
this.state = State.STOPPED;
this.maxFloor = maxFloor;
this.minFloor = minFloor;
}
public Elevator getElevatorInstance(int minFloor, int maxFloor) {
return new Elevator(minFloor, maxFloor);
}
public int getCurrentFloor() {
return currentFloor;
}
public void setCurrentFloor(int currentFloor) {
this.currentFloor = currentFloor;
}
public Direction getDirection() {
return direction;
}
public void setDirection(Direction direction) {
this.direction = direction;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public int getMaxFloor() {
return maxFloor;
}
public int getMinFloor() {
return minFloor;
}
public boolean isValidFloor(int floor) {
return floor >= minFloor && floor <= maxFloor;
}
public void move() {
if (direction == Direction.UP) {
currentFloor++;
} else {
currentFloor--;
}
System.out.println("Current Floor: " + currentFloor + " Direction: " + direction);
state = State.MOVING;
}
public void openDoors() {
System.out.println("Open Door # Floor " + currentFloor);
state = State.STOPPED;
}
public void closeDoors() {
System.out.println("Close Door");
state = State.MOVING;
}
}
// 控制类
class Controller {
private Elevator elevator;
private RequestQueue queue;
public Controller() {
}
public Controller(Elevator elevator, RequestQueue requestQueue) {
this.elevator = elevator;
this.queue = requestQueue;
}
public Elevator getElevator() {
return elevator;
}
public void setElevator(Elevator elevator) {
this.elevator = elevator;
}
public RequestQueue getQueue() {
return queue;
}
public void setQueue(RequestQueue queue) {
this.queue = queue;
}
public void processRequests() {
System.out.println("Current Floor: " + elevator.getCurrentFloor() + " Direction: " + elevator.getDirection());
while (!queue.getInternalRequests().isEmpty() || !queue.getExternalRequests().isEmpty()) {
determineDirection();
elevator.move();
if (shouldStop(elevator.getCurrentFloor())) {
elevator.openDoors();
removeRequests(elevator.getCurrentFloor());
elevator.closeDoors();
}
}
}
private void determineDirection() {
LinkedList<Passenger> internalReqs = queue.getInternalRequests();
LinkedList<Passenger> externalReqs = queue.getExternalRequests();
int currentFloor = elevator.getCurrentFloor();
if (!internalReqs.isEmpty()) {
Passenger firstInternal = internalReqs.getFirst();
if (firstInternal.getDestinationFloor() > currentFloor) {
elevator.setDirection(Direction.UP);
} else if (firstInternal.getDestinationFloor() < currentFloor) {
elevator.setDirection(Direction.DOWN);
}
} else if (!externalReqs.isEmpty()) {
Passenger firstExternal = externalReqs.getFirst();
if (firstExternal.getDestinationFloor() > currentFloor) {
elevator.setDirection(Direction.UP);
} else if (firstExternal.getDestinationFloor() < currentFloor) {
elevator.setDirection(Direction.DOWN);
}
}
}
private boolean shouldStop(int floor) {
LinkedList<Passenger> internalReqs = queue.getInternalRequests();
LinkedList<Passenger> externalReqs = queue.getExternalRequests();
for (Passenger p : internalReqs) {
if (p.getDestinationFloor() == floor) {
return true;
}
}
for (Passenger p : externalReqs) {
if (p.getSourceFloor() == floor) {
return true;
}
}
return false;
}
private void removeRequests(int currentFloor) {
LinkedList<Passenger> internalReqs = queue.getInternalRequests();
LinkedList<Passenger> externalReqs = queue.getExternalRequests();
// 处理内部请求
for (Passenger p : internalReqs) {
if (p.getDestinationFloor() == currentFloor) {
internalReqs.remove(p);
break;
}
}
// 处理外部请求
for (Passenger p : externalReqs) {
if (p.getSourceFloor() == currentFloor) {
queue.addInternalRequest(new Passenger(p.getDestinationFloor()));
externalReqs.remove(p);
break;
}
}
}
}
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();
RequestQueue queue = new RequestQueue();
Elevator elevator = new Elevator(minFloor, maxFloor);
Controller controller = new Controller(elevator, queue);
String input;
while (!(input = scanner.nextLine().trim()).equalsIgnoreCase("end")) {
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);
queue.addExternalRequest(passenger);
} else {
int destinationFloor = Integer.parseInt(input.replaceAll("[<>]", ""));
Passenger passenger = new Passenger(destinationFloor);
queue.addInternalRequest(passenger);
}
}
controller.processRequests();
scanner.close();
}
}
类图:
代码分析:
代码总体分析
从图片提供的代码度量信息来看,这是对一个名为 “电梯 8.txt” 文件(推测是 Java 代码文件)的分析结果。以下是各项指标的解读:
基本信息:文件包含 296 行代码,其中有 167 条语句,分支语句占比 13.2% ,方法调用语句有 60 条 ,注释行占比仅 2.7% 。定义了 2 个类和接口,平均每个类有 20 个方法,每个方法平均有 4.03 条语句 。
复杂度相关:最复杂方法是 Main.main() ,位于 176 行,最大复杂度为 4 ,最深代码块在 277 行 。
存在问题
注释过少:仅 2.7% 的注释行占比,这会导致代码可读性极差。当其他开发人员接手代码,或者自己后续维护时,很难快速理解代码逻辑,尤其是复杂的电梯调度业务逻辑部分,理解成本大幅增加。
类的设计:平均每个类有 20 个方法,可能存在类的职责不够单一的情况。虽然题目要求遵循单一职责原则,但从方法数量来看,或许某些类承担了过多功能,导致代码臃肿,不利于维护和扩展。
Main.main()方法复杂度过高:Main.main() 方法是最复杂的方法,复杂度达到 4 。main 方法通常应该是程序的入口,起到初始化和调用其他模块的作用,不应该承担过多复杂业务逻辑。它过于复杂可能意味着没有将业务逻辑合理拆分到其他类和方法中。
三、总结
在完成这一系列电梯调度程序作业的过程中,我在 Java 知识学习和编程实践方面都收获颇丰,也积累了宝贵的经验教训。希望我在下次大作业可以取得更好的成绩,不至于一塌糊涂(哭)
一、知识技能提升
(一)Java 语法与基础结构掌握
通过编写电梯相关程序,我对 Java 的基础语法和结构有了更深入、更实际的运用理解。在定义电梯类(Elevator)、乘客类(Passenger)等各类时,对类的属性和方法的设计与封装有了深刻体会。比如电梯类中,精确设置当前楼层、运行方向等属性,以及移动、开关门等方法,清晰界定了电梯对象的特征与行为,这让我对类与对象的关系有了从理论到实践的跨越。
对于枚举类型的运用也更加得心应手。使用枚举(如 Direction 枚举表示电梯运行方向)来定义有限的、固定的取值集合,不仅提升了代码的可读性,使其更加直观易懂,还增强了代码的安全性,有效避免了非法值的出现。
(二)集合框架与数据结构应用
在管理乘客请求队列时,LinkedList 集合的使用让我对 Java 集合框架有了切实的操作经验。我明白了根据实际需求(如频繁的插入和删除操作)选择合适集合类的重要性,也熟练掌握了集合的基本操作,如添加元素、获取元素等。这不仅加深了我对不同集合类性能特点的理解,也让我在处理数据存储和操作时有了更清晰的思路。
(三)方法设计与程序流程控制
在设计电梯运行相关方法(如电梯移动方法 move ()、控制类中确定方向方法 determineDirection () 等)时,我学会了如何合理规划方法的参数传递和返回值处理,以实现类与类之间的有效交互和数据传递,从而完成复杂的业务逻辑。同时,在整个程序流程的控制上,也更加明晰如何协调各个类和方法的执行顺序,以保障程序的正常运行。
二、编程实践经验
(一)前期规划的重要性
起初,由于缺乏对程序整体结构的充分规划,在编码过程中频繁出现类结构和方法设计不合理的情况,导致代码逻辑混乱,后期重构耗费了大量时间和精力。这让我深刻认识到,在编程前进行详细设计(如绘制类图、流程图,明确类的职责和交互关系)的重要性,它是构建清晰、高效代码的基石。
(二)注重代码可读性与注释
之前对代码注释重视不足,导致一段时间后自己对部分复杂逻辑都难以快速理解。这使我明白,清晰的注释是代码可读性的重要保障,无论是对自己后期维护,还是与他人交流代码,详尽的注释都能极大降低理解成本,提升沟通效率。
用户点评