5~7题目集之单部电梯
前言
这三次题目集主要是有关于电梯实现的一个迭代过程,这与之前的题目相比可以说难度是直线飙升的。😅
第一次电梯调试要求我们只用一个电梯类,所以主要解决的就是算法问题以及需求分析,而其主要涉及略微修改的Look算法[1](方向优先,即优先处理同方向的需求),此外我还学会了如何使用正则表达式(这真的是用来处理String的一大利器啊😄,这是在C语言处理字符串时没有体验过方便与爽快);
第二次电梯调试则要求在第一次代码的基础上根据单一职责原则分成电梯类、控制类、请求队列类、外部需求类,这就需要我们能够画出类图,分析一个类该有哪些属性和方法以及类与类之间有着什么样的关系(依赖、关联、聚合与组合)。
第三次电梯题目则就是将需求发生了一点改变,外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾),这实际考验的是当顾客需求发生变化时,如何在原有的基础上进行最小化修改,这就是老师常说的做我们这一行的要不惧变化,而要拥抱变化。
总的来说这三次迭代的难度和题量还是适中的,最难的就是开始的从无到有,要从C语言的编码方式转向以Java的方式编码,要理解需求并设计出合适的算法,在最开始时我优先选择的是用最熟悉的数组来进行存储和处理需求,但第二次迭代时我了解到Java集合框架中有类似于C语言中链表的列表LinkList,它可以更好的存储外部请求(包括楼层与方向),也能更方便地删除已处理完的需求。
设计与分析
第一次题目
点击查看第一次代码
import java.util.Scanner;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
// 主类,程序的入口点
public class Main {
public static void main(String[] args) {
// 创建一个 Scanner 对象,用于从标准输入读取数据
Scanner input = new Scanner(System.in);
// 读取最低楼层数
int lowestStair = input.nextInt();
// 读取最高楼层数
int highestStair = input.nextInt();
// 定义结束标志字符串
String s = "end";
// 用于存储输入的请求,最大长度为 100
String[] request;
int i = 0;
request = new String[100];
// 循环读取输入,直到遇到结束标志 "end"
do {
request[i] = input.next();
i++;
} while (request[i - 1].compareToIgnoreCase(s) != 0);
// 创建一个 Stair 对象,初始化最低楼层、最高楼层和当前楼层
Stair stair = new Stair(lowestStair, highestStair, 1);
// 调用 turncharToint 方法,将输入的请求字符串转换为整数数组
stair.turncharToint(request, i);
// 调用 Print 方法,打印初始状态信息
stair.Print();
// 调用 checkCurrentrequire 方法,检查并处理当前的请求
stair.checkCurrentrequire();
//stair.Print();
}
}
// 电梯类,用于模拟电梯的运行
class Stair {
// 最低楼层
private int lowestStair;
// 最高楼层
private int highestStair;
// 当前楼层,初始为 1
private int currentStair = 1;
// 电梯运行状态,0 表示静止,1 表示向上,2 表示向下
private int updownState = 0;
// 存储向上请求的楼层
private int[] a = new int[100];
// 存储向上请求的楼层对应的方向标志
private int[] b = new int[100];
// 存储内部请求的楼层
private int[] c = new int[100];
// 无参构造函数
Stair() {
}
// 有参构造函数,初始化最低楼层、最高楼层和当前楼层
Stair(int lowestStair, int highestStair, int currentStair) {
this.lowestStair = lowestStair;
this.highestStair = highestStair;
this.currentStair = currentStair;
}
// 根据请求楼层控制电梯的运行状态
public void controlupdownState(int requestfloor) {
if (requestfloor > this.currentStair) {
// 请求楼层高于当前楼层,电梯向上运行
this.updownState = 1;
} else if (requestfloor < this.currentStair) {
// 请求楼层低于当前楼层,电梯向下运行
this.updownState = 2;
} else {
// 请求楼层等于当前楼层,电梯静止
this.updownState = 0;
}
}
// 输出电梯状态
public void printState(int floor) {
// 检查请求楼层是否在有效范围内
if (checkFloor(floor)) {
if (floor == this.currentStair) {
// 请求楼层等于当前楼层,打开和关闭电梯门
System.out.printf("Open Door # Floor %d\nClose Door\n", this.currentStair, this.currentStair);
} else {
// 请求楼层高于当前楼层,电梯向上运行
while (floor > this.currentStair) {
this.currentStair++;
if (floor > this.currentStair) {
System.out.printf("Current Floor: %d Direction: UP\n", this.currentStair);
}
}
// 请求楼层低于当前楼层,电梯向下运行
while (floor < this.currentStair) {
this.currentStair--;
if (floor < this.currentStair) {
System.out.printf("Current Floor: %d Direction: DOWN\n", this.currentStair);
}
}
if (floor == this.currentStair) {
// 到达请求楼层,输出当前楼层、运行方向、打开和关闭电梯门信息
System.out.printf("Current Floor: %d Direction: %s\nOpen Door # Floor %d\nClose Door", this.currentStair,
this.updownState == 1 ? "UP" : "DOWN", floor);
}
if (a[0] != 0 || c[0] != 0) {
System.out.printf("\n");
}
}
}
}
// 将输入的请求字符串转换为整数数组
public void turncharToint(String[] request, int i) {
int j = 0;
int m = 0;
int n = 0;
// 遍历输入的请求字符串数组
for (; j < i - 1; j++) {
// 定义匹配向上请求的正则表达式
String regStr1 = "<(\\d+),UP>";
// 定义匹配向下请求的正则表达式
String regStr2 = "<(\\d+),DOWN>";
// 定义匹配内部请求的正则表达式
String regStr3 = "<(\\d+)>";
// 编译正则表达式
Pattern pattern1 = Pattern.compile(regStr1);
Matcher matcher1 = pattern1.matcher(request[j]);
Pattern pattern2 = Pattern.compile(regStr2);
Matcher matcher2 = pattern2.matcher(request[j]);
Pattern pattern3 = Pattern.compile(regStr3);
Matcher matcher3 = pattern3.matcher(request[j]);
if (matcher1.find()) {
// 匹配到向上请求,将楼层号存入数组 a,并将方向标志存入数组 b
a[m] = Integer.parseInt(matcher1.group(1));
b[m] = 0;
m++;
} else if (matcher2.find()) {
// 匹配到向下请求,将楼层号存入数组 a,并将方向标志存入数组 b
a[m] = Integer.parseInt(matcher2.group(1));
b[m] = 1;
m++;
} else if (matcher3.find()) {
// 匹配到内部请求,将楼层号存入数组 c
c[n] = Integer.parseInt(matcher3.group(1));
n++;
}
}
}
// 清除数组 a 和 b 中的第一个元素
public void clearAb() {
int i;
for (i = 0; a[i + 1] != 0; i++) {
a[i] = a[i + 1];
b[i] = b[i + 1];
}
a[i] = a[i + 1];
b[i] = b[i + 1];
}
// 清除数组 c 中的第一个元素
public void clearC() {
int i = 0;
for (; c[i + 1] != 0; i++) {
c[i] = c[i + 1];
}
c[i] = c[i + 1];
}
// 检查并处理当前的请求
public void checkCurrentrequire() {
int floor = 1;
int i = 0, j = 0;
// 循环处理请求,直到数组 a 和 c 都为空
for (; a[0] != 0 || c[0] != 0;) {
switch (updownState) {
case 0:
// 电梯静止状态
if (a[i] != 0 && c[j] != 0) {
if (Math.abs(a[i] - this.currentStair) < Math.abs(c[j] - this.currentStair)) {
if (b[0] == 0) {
// 向上请求距离当前楼层更近,处理向上请求
controlupdownState(a[i]);
floor = a[i];
clearAb();
printState(floor);
} else {
// 向下请求距离当前楼层更近,处理内部请求
controlupdownState(c[0]);
floor = c[0];
clearC();
printState(floor);
}
} else if (Math.abs(a[i] - this.currentStair) > Math.abs(c[j] - this.currentStair)) {
// 内部请求距离当前楼层更近,处理内部请求
controlupdownState(c[j]);
floor = c[j];
clearC();
printState(floor);
} else {
// 向上请求和内部请求距离当前楼层相等,处理向上请求
floor = a[i];
controlupdownState(a[i]);
clearC();
clearAb();
printState(floor);
}
} else if (a[i] != 0) {
// 只有向上请求,处理向上请求
controlupdownState(a[0]);
floor = a[0];
clearAb();
printState(floor);
} else if (c[j] != 0) {
// 只有内部请求,处理内部请求
controlupdownState(c[0]);
floor = c[j];
clearC();
printState(floor);
}
break;
case 1:
// 电梯向上运行状态
if (a[i] != 0 && c[j] != 0) {
if (a[i] > this.currentStair && c[j] > this.currentStair) {
if (a[i] > c[j]) {
// 内部请求楼层低于向上请求楼层,处理内部请求
floor = c[j];
clearC();
printState(floor);
} else if (a[i] < c[j] && b[i] == 0) {
// 向上请求楼层低于内部请求楼层,且为向上请求,处理向上请求
floor = a[i];
clearAb();
printState(floor);
} else if (a[i] < c[j] && b[i] != 0) {
// 向上请求楼层低于内部请求楼层,且为向下请求,处理内部请求
floor = c[j];
clearC();
printState(floor);
} else if (a[i] == c[j]) {
// 向上请求楼层等于内部请求楼层,处理请求
floor = c[j];
clearAb();
clearC();
printState(floor);
}
} else if (a[i] > this.currentStair) {
// 只有向上请求楼层高于当前楼层,处理向上请求
floor = a[i];
clearAb();
printState(floor);
} else if (c[j] > this.currentStair) {
// 只有内部请求楼层高于当前楼层,处理内部请求
floor = c[i];
clearC();
printState(floor);
} else if (c[j] < this.currentStair && a[i] < this.currentStair) {
// 所有请求楼层都低于当前楼层,改变电梯运行状态为向下
updownState = 2;
}
} else if (a[0] != 0) {
// 只有向上请求,处理向上请求
controlupdownState(a[i]);
floor = a[i];
clearAb();
printState(floor);
} else if (c[0] != 0) {
// 只有内部请求,处理内部请求
controlupdownState(c[0]);
floor = c[0];
clearC();
printState(floor);
}
break;
case 2:
// 电梯向下运行状态
if (a[0] != 0 && c[0] != 0) {
if (a[0] < this.currentStair && c[0] < this.currentStair) {
if (a[0] < c[0]) {
// 内部请求楼层高于向下请求楼层,处理内部请求
floor = c[0];
clearC();
printState(floor);
} else if (a[0] > c[0] && b[0] == 1) {
// 向下请求楼层高于内部请求楼层,且为向下请求,处理向下请求
floor = a[
0]; clearAb();
printState(floor);
} else if (a[0] > c[0] && b[0] != 1) {
// 向下请求楼层高于内部请求楼层,且为向上请求,处理内部请求
floor = c[0];
clearC();
printState(floor);
} else if (a[0] == c[0]) {
// 向下请求楼层等于内部请求楼层,处理请求
floor = c[0];
clearAb();
clearC();
printState(floor);
}
} else if (a[0] < this.currentStair) {
// 只有向下请求楼层低于当前楼层,处理向下请求
floor = a[0];
clearAb();
printState(floor);
} else if (c[0] < this.currentStair) {
// 只有内部请求楼层低于当前楼层,处理内部请求
floor = c[0];
clearC();
printState(floor);
} else if (a[i] > this.currentStair && c[j] > this.currentStair) {
// 所有请求楼层都高于当前楼层,改变电梯运行状态为向上
updownState = 1;
}
} else if (a[0] != 0) {
// 只有向下请求,处理向下请求
controlupdownState(a[0]);
floor = a[0];
clearAb();
printState(floor);
} else if (c[0] != 0) {
// 只有内部请求,处理内部请求
controlupdownState(c[0]);
floor = c[0];
clearC();
printState(floor);
}
break;
}
}
}
// 检查请求楼层是否在有效范围内
public boolean checkFloor(int floor) {
if (floor < lowestStair || floor > highestStair) {
return false;
} else {
return true;
}
}
// 打印初始状态信息
public void Print() {
while (a[0] != 0) {
if (checkFloor(a[0]) && a[0] != 1) {
// 向上请求楼层有效且不等于当前楼层,输出电梯向上运行信息
System.out.printf("Current Floor: 1 Direction: UP\n");
break;
} else if (checkFloor(a[0])) {
break;
} else {
// 向上请求楼层无效,清除该请求
clearAb();
}
}
}
}
第一次代码类图:

第一次代码分析:
SM分析表:

表格解释与分析:
解释:
代码规模相关
- Lines:代码总行数 339 行。
- Statements:语句总数 227 条 ,反映代码中执行单元数量。
- Classes and Interfaces:类和接口数量为 2 ,说明代码中定义了 2 个相关结构。
- Methods per Class:平均每个类的方法数是 5.00 ,体现类的功能丰富度。
- Average Statements per Method:每个方法平均语句数 19.40 ,衡量方法的代码量。
代码结构相关 - Percent Branch Statements:分支语句占比 27.3% ,即if、switch等产生分支逻辑的语句在总语句中的占比,反映代码逻辑分支的频繁程度。
- Method Call Statements:方法调用语句有 90 条 ,展示代码中方法间调用的活跃度。
- Percent Lines with Comments:含注释的代码行占比 12.7% ,说明代码注释量的多少。
复杂度相关 - Line Number of Most Complex Method:最复杂方法所在行号 43 。
- Name of Most Complex Method:最复杂方法为Stair.controlupdownState() 。
- Maximum Complexity:最大复杂度为 4,表示代码中最复杂方法的复杂度数值。
- Line Number of Deepest Block:最深代码块所在行号 149 ,反映深度嵌套代码块位置。
- Maximum Block Depth:最大代码块深度 7 ,即代码中嵌套层次最深达到 7 层。
- Average Block Depth:平均代码块深度 3.87 ,体现整体代码嵌套的平均程度。
- Average Complexity:平均复杂度 1.75,代表所有方法复杂度的平均值。
分析:
规模相关指标
- Statements(227 条):即代码中可执行语句数量。它比行数更精准反映代码执行量,平均每行约 0.67 条语句,说明代码密度适中。
- Classes and Interfaces(2 个):代码中定义了 2 个类或接口,说明代码结构有一定封装性,通过不同类型结构组织功能。
- Methods per Class(5.00):平均每个类有 5 个方法,表明类的功能相对丰富,有一定内聚性,通过多个方法实现类的职责,但也需关注方法间耦合情况。
- Average Statements per Method(19.40):每个方法平均含 19.4 条语句,方法规模不算过小也未过大,不过仍需结合逻辑复杂度判断是否符合单一职责原则。
结构相关指标 - Percent Branch Statements(27.3%):分支语句(如if、switch )占总语句比例为 27.3% ,说明代码中存在一定逻辑分支,但并非过度复杂,不过可审视分支逻辑是否可优化。
- Method Call Statements(90 条):方法调用语句有 90 条,反映代码中方法间交互频繁,合理的方法调用可提高代码复用性和模块化,但过多或不合理调用导致高耦合。
- Percent Lines with Comments(21.7%):注释行占比适中,适当的注释能增强代码可读性和可维护性。
复杂度相关指标 - Line Number of Most Complex Method(43 行):最复杂方法在第 43 行,可定位到具体位置针对性优化。
- Name of Most Complex Method(Stair.controlupdownState() ):明确最复杂方法是Stair类中的controlupdownState方法,需深入分析该方法逻辑,考虑是否可拆分或优化。
- Maximum Complexity(4 ):最大复杂度为 4 ,圈复杂度 4 相对不算极高,但仍可进一步优化,降低理解和维护难度。
- Line Number of Deepest Block(149 行):最深代码块在 149 行,提示此处嵌套层次深,可能存在可读性和维护性问题。
- Maximum Block Depth(7 ):最大代码块深度 7 层,嵌套过深,需考虑重构,比如提取子方法、使用设计模式简化逻辑。
- Average Block Depth(3.87 ):平均代码块深度 3.87 ,说明整体嵌套情况较普遍,有优化空间。
- Average Complexity(1.75 ):平均复杂度 1.75 ,整体方法复杂度不算高,但结合最大复杂度和深度指标,部分方法仍需优化。
心得:
checkCurrentrequire 方法逻辑复杂,嵌套层次深,对单一职责原则应用还不够熟练,应将不同状态下的处理逻辑拆分成独立的方法,提高代码的可读性和可维护性,并且使用的是定长数组,可能会造成内存的浪费或越界。而此次作业呢大家僵持了好久,始终没有人通过测试点,到后来老师提点一下就开始陆陆续续有人通过了,因为很多人一开始的思路就错了,没有去分析题目给的需求,而是自动带入到现实生活中的电梯运行方式,而导致算法错误。但我是作为前20个通过的,为什么呢?因为我一开始不是一股脑地直接开始写代码,而是先构思好算法,刚开始我也自动带入了现实并且有点钻牛角尖(总是想一些极端情况😏),在这种情况下我静下心来研究需求,这时我意识到题目中的需求与现实生活是不太一样的,等构思好算法后我才开始写,所以我的第一次代码方向和算法就是正确的,写完后给出的测试用例能够通过,但答案还是错误,说明算法可能还存在一些细节上的问题,然后我就开始测试一些别的用例,发现了一处缺陷,经过修改后顺利通过😄。
第二次题目
点击查看第二次代码
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.LinkedList;
// 主类,程序的入口点
public class Main {
public static void main(String[] args) {
// 创建 Scanner 对象,用于从标准输入读取数据
Scanner input = new Scanner(System.in);
// 读取最低楼层数
int lowestStair = input.nextInt();
// 读取最高楼层数
int highestStair = input.nextInt();
// 定义结束标志字符串
String s = "end";
// 用于存储输入的请求,最大长度为 100
String[] request;
request = new String[100];
// 读取第一个请求
request[0] = input.next();
int i = 1;
// 循环读取输入,直到遇到结束标志 "end",并去除重复请求
while (request[i - 1].compareToIgnoreCase(s) != 0) {
String t = input.next();
if (!request[i - 1].equals(t)) {
request[i] = t;
i++;
}
}
// 创建电梯对象,传入最低楼层和最高楼层
Elevator elevator = new Elevator(lowestStair, highestStair);
// 创建请求队列对象
RequestQueue requestQueue = new RequestQueue();
int j = 0;
// 遍历输入的请求字符串数组
for (; j < i - 1; j++) {
// 定义匹配向上请求的正则表达式
String regStr1 = "<(\\d+),UP>";
// 定义匹配向下请求的正则表达式
String regStr2 = "<(\\d+),DOWN>";
// 定义匹配内部请求的正则表达式
String regStr3 = "<(\\d+)>";
// 编译正则表达式
Pattern pattern1 = Pattern.compile(regStr1);
Matcher matcher1 = pattern1.matcher(request[j]);
Pattern pattern2 = Pattern.compile(regStr2);
Matcher matcher2 = pattern2.matcher(request[j]);
Pattern pattern3 = Pattern.compile(regStr3);
Matcher matcher3 = pattern3.matcher(request[j]);
if (matcher1.find()) {
// 匹配到向上请求,检查楼层是否在有效范围内
if (Integer.parseInt(matcher1.group(1)) <= highestStair && Integer.parseInt(matcher1.group(1)) >= lowestStair) {
// 将有效的向上请求添加到请求队列中
requestQueue.addExternalRequest(Integer.parseInt(matcher1.group(1)), Direction.UP);
}
} else if (matcher2.find()) {
// 匹配到向下请求,检查楼层是否在有效范围内
if (Integer.parseInt(matcher2.group(1)) <= highestStair && Integer.parseInt(matcher2.group(1)) >= lowestStair) {
// 将有效的向下请求添加到请求队列中
requestQueue.addExternalRequest(Integer.parseInt(matcher2.group(1)), Direction.DOWN);
}
} else if (matcher3.find()) {
// 匹配到内部请求,检查楼层是否在有效范围内
if (Integer.parseInt(matcher3.group(1)) <= highestStair && Integer.parseInt(matcher3.group(1)) >= lowestStair) {
// 将有效的内部请求添加到请求队列中
requestQueue.addInternalRequests(Integer.parseInt(matcher3.group(1)));
}
}
}
// 创建控制器对象,传入电梯对象和请求队列对象
Controller controller = new Controller(elevator, requestQueue);
// 打印电梯初始状态信息
controller.print();
// 处理请求队列中的下一个请求
controller.processNextRequest();
}
}
// 控制器类,负责协调电梯和请求队列之间的交互
class Controller {
// 电梯对象
private Elevator elevator;
// 请求队列对象
private RequestQueue requestQueue;
// 无参构造函数
Controller() {
}
// 有参构造函数,初始化电梯和请求队列
Controller(Elevator elevator, RequestQueue requestQueue) {
this.elevator = elevator;
this.requestQueue = requestQueue;
}
// 获取电梯对象
public Elevator getElevator() {
return this.elevator;
}
// 设置电梯对象
public void setElevator(Elevator elevator) {
this.elevator = elevator;
}
// 获取请求队列对象
public RequestQueue getRequestQueue() {
return this.requestQueue;
}
// 设置请求队列对象
public void setRequestQueue(RequestQueue requestQueue) {
this.requestQueue = requestQueue;
}
// 打印电梯初始状态信息
public void print() {
System.out.print("Current Floor: 1 Direction: UP\n");
}
// 处理请求队列中的下一个请求
public void processNextRequest() {
// 当内部请求队列或外部请求队列不为空时,继续处理请求
while (!this.requestQueue.getInternalRequests().isEmpty() || !this.requestQueue.getExternalRequests().isEmpty()) {
if (!this.requestQueue.getInternalRequests().isEmpty() && !this.requestQueue.getExternalRequests().isEmpty()) {
// 处理内部请求和外部请求都存在的情况
int floor = process();
if (floor == 0) {
// 如果返回 0,改变电梯运行方向
turnDirection();
} else if (floor == -1) {
// 如果返回 -1,处理外部请求直到到达指定楼层
while (true) {
if (shouldStop(this.requestQueue.getExternalRequests().get(0).getFloor())) {
// 到达指定楼层,移除请求并打开电梯门
removeRequest(this.elevator.getCurrentFloor());
openDoors();
break;
}
// 电梯移动
move();
}
// 改变电梯运行方向
turnDirection();
} else {
// 处理其他情况,直到到达指定楼层
while (true) {
if (shouldStop(floor)) {
// 到达指定楼层,移除请求并打开电梯门
removeRequest(this.elevator.getCurrentFloor());
openDoors();
break;
}
// 电梯移动
move();
}
}
} else if (!this.requestQueue.getInternalRequests().isEmpty()) {
// 只有内部请求的情况
int a = this.requestQueue.getInternalRequests().get(0) - this.elevator.getCurrentFloor();
if (a > 0) {
// 请求楼层高于当前楼层,电梯向上运行
this.elevator.setDirection(Direction.UP);
} else {
// 请求楼层低于当前楼层,电梯向下运行
this.elevator.setDirection(Direction.DOWN);
}
// 处理内部请求直到到达指定楼层
while (true) {
if (shouldStop(this.requestQueue.getInternalRequests().get(0))) {
// 到达指定楼层,移除请求并打开电梯门
removeRequest(this.elevator.getCurrentFloor());
openDoors();
break;
}
// 电梯移动
move();
}
} else if (!this.requestQueue.getExternalRequests().isEmpty()) {
// 只有外部请求的情况
int a = this.requestQueue.getExternalRequests().get(0).getFloor() - this.elevator.getCurrentFloor();
if (a > 0) {
// 请求楼层高于当前楼层,电梯向上运行
this.elevator.setDirection(Direction.UP);
} else {
// 请求楼层低于当前楼层,电梯向下运行
this.elevator.setDirection(Direction.DOWN);
}
// 处理外部请求直到到达指定楼层
while (true) {
if (shouldStop(this.requestQueue.getExternalRequests().get(0).getFloor())) {
// 到达指定楼层,移除请求并打开电梯门
removeRequest(this.elevator.getCurrentFloor());
openDoors();
break;
}
// 电梯移动
move();
}
}
}
}
// 处理内部请求和外部请求都存在的情况,返回要到达的楼层
private int process() {
int flag = 0;
// 计算外部请求楼层与当前楼层的差值
int a = this.requestQueue.getExternalRequests().get(0).getFloor() - this.elevator.getCurrentFloor();
// 计算内部请求楼层与当前楼层的差值
int b = this.requestQueue.getInternalRequests().get(0) - this.elevator.getCurrentFloor();
if (this.elevator.getDirection().equals(Direction.UP)) {
// 电梯向上运行的情况
if (a > 0 && b > 0) {
// 外部请求和内部请求楼层都高于当前楼层
if (a >= b) {
// 内部请求楼层更近,返回内部请求楼层
flag = this.requestQueue.getInternalRequests().get(0);
} else {
// 外部请求楼层更近
if (this.requestQueue.getExternalRequests().get(0).getDirection().equals(Direction.UP)) {
// 外部请求为向上请求,返回外部请求楼层
flag = this.requestQueue.getExternalRequests().get(0).getFloor();
} else {
// 外部请求为向下请求,返回内部请求楼层
flag = this.requestQueue.getInternalRequests().get(0);
}
}
} else if (a > 0 && b < 0) {
// 外部请求楼层高于当前楼层,内部请求楼层低于当前楼层
if (this.requestQueue.getExternalRequests().get(0).getDirection().equals(Direction.UP)) {
// 外部请求为向上请求,返回外部请求楼层
flag = this.requestQueue.getExternalRequests().get(0).getFloor();
} else {
// 外部请求为向下请求,返回 -1
flag = -1;
}
} else if (a < 0 && b > 0) {
// 外部请求楼层低于当前楼层,内部请求楼层高于当前楼层,返回内部请求楼层
flag = this.requestQueue.getInternalRequests().get(0);
}
} else if (this.elevator.getDirection().equals(Direction.DOWN)) {
// 电梯向下运行的情况
if (a < 0 && b < 0) {
// 外部请求和内部请求楼层都低于当前楼层
if (a <= b) {
// 内部请求楼层更近,返回内部请求楼层
flag = this.requestQueue.getInternalRequests().get(0);
} else {
// 外部请求楼层更近
if (this.requestQueue.getExternalRequests().get(0).getDirection().equals(Direction.DOWN)) {
// 外部请求为向下请求,返回外部请求楼层
flag = this.requestQueue.getExternalRequests().get(0).getFloor();
} else {
// 外部请求为向上请求,返回内部请求楼层
flag = this.requestQueue.getInternalRequests().get(0);
}
}
} else if (a < 0 && b > 0) {
// 外部请求楼层低于当前楼层,内部请求楼层高于当前楼层
if (this.requestQueue.getExternalRequests().get(0).getDirection().equals(Direction.UP)) {
// 外部请求为向上请求,返回 -1
flag = -1;
} else {
// 外部请求为向下请求,返回外部请求楼层
flag = this.requestQueue.getExternalRequests().get(0).getFloor();
}
} else if (a > 0 && b < 0) {
// 外部请求楼层高于当前楼层,内部请求楼层低于当前楼层,返回内部请求楼层
flag = this.requestQueue.getInternalRequests().get(0);
}
}
return flag;
}
// 电梯移动方法
private void move() {
if (this.elevator.getDirection().equals(Direction.UP)) {
// 电梯向上运行,更新当前楼层并打印信息
this.elevator.setCurrentFloor(this.getNextFloor());
System.out.printf("Current Floor: %d Direction: UP\n", this.elevator.getCurrentFloor());
} else if (this.elevator.getDirection().equals(Direction.DOWN)) {
// 电梯向下运行,更新当前楼层并打印信息
this.elevator.setCurrentFloor(this.getNextFloor());
System.out.printf("Current Floor: %d Direction: DOWN\n", this.elevator.getCurrentFloor());
}
}
// 判断是否应该停止电梯
private boolean shouldStop(int floor) {
if (floor == this.elevator.getCurrentFloor()) {
// 到达指定楼层,返回 true
return true;
} else {
// 未到达指定楼层,返回 false
return false;
}
}
// 获取下一个楼层
private Integer getNextFloor() {
int nextfloor = 1;
if (this.elevator.getDirection().equals(Direction.UP)) {
// 电梯向上运行,下一个楼层为当前楼层加 1
nextfloor = this.elevator.getCurrentFloor() + 1;
} else if (this.elevator.getDirection().equals(Direction.DOWN)) {
// 电梯向下运行,下一个楼层为当前楼层减 1
nextfloor = this.elevator.getCurrentFloor() - 1;
}
return nextfloor;
}
// 打开电梯门并打印信息
private void openDoors() {
System.out.printf("Open Door # Floor %d\nClose Door", this.elevator.getCurrentFloor());
if (this.requestQueue.getInternalRequests() != null || this.requestQueue.getExternalRequests() != null) {
// 如果还有请求,换行
System.out.print("\n");
}
}
// 移除指定楼层的请求
private void removeRequest(int currentFloor) {
if (!this.requestQueue.getExternalRequests().isEmpty()) {
if (this.getRequestQueue().getExternalRequests().get(0).getFloor() == currentFloor) {
// 移除外部请求队列中的第一个请求
this.getRequestQueue().getExternalRequests().remove(0);
}
}
if (!this.requestQueue.getInternalRequests().isEmpty()) {
if (this.getRequestQueue().getInternalRequests().get(0) == currentFloor) {
// 移除内部请求队列中的第一个请求
this.getRequestQueue().getInternalRequests().remove(0);
}
}
}
// 改变电梯运行方向
private void turnDirection() {
if (this.elevator.getDirection().equals(Direction.UP)) {
// 电梯向上运行,改为向下运行
this.elevator.setDirection(Direction.DOWN);
} else {
// 电梯向下运行,改为向上运行
this.elevator.setDirection(Direction.UP);
}
}
}
// 电梯类,代表电梯的状态和属性
class Elevator {
// 最低楼层
private int lowestStair;
// 最高楼层
private int highestStair;
// 当前楼层,初始为 1
private int currentFloor = 1;
// 电梯运行方向,初始为空闲
private Direction direction = Direction.IDLE;
// 电梯状态,初始为停止
private State state = State.STOPPED;
// 有参构造函数,初始化最低楼层、最高楼层
Elevator(int lowestStair, int highestStair) {
this.lowestStair = lowestStair;
this.highestStair = highestStair;
}
// 获取当前楼层
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 this.state;
}
// 设置电梯状态
public void setState(State state) {
this.state = state;
}
// 获取最高楼层
public int getHighestStair() {
return highestStair;
}
// 获取最低楼层
public int getLowestStair() {
return lowestStair;
}
// 判断楼层是否有效
public boolean isValidFloor(int floor) {
if (floor >= this.lowestStair && floor <= this.highestStair) {
// 楼层在有效范围内,返回 true
return true;
} else {
// 楼层不在有效范围内,返回 false
return false;
}
}
}
// 外部请求类,代表电梯外部的请求
class ExternalRequest {
// 请求的楼层
private Integer floor;
// 请求的方向
private Direction direction;
// 有参构造函数,初始化请求楼层和方向
ExternalRequest(Integer floor, Direction direction) {
this.floor = floor;
this.direction = direction;
}
// 获取请求的楼层
public Integer getFloor() {
return floor;
}
// 获取请求的方向
public Direction getDirection() {
return direction;
}
}
// 请求队列类,管理内部请求和外部请求
class RequestQueue {
// 内部请求队列
private LinkedList<Integer> internalRequests = new LinkedList<>();
// 外部请求队列
private LinkedList<ExternalRequest> externalRequests = new LinkedList<>();
// 无参构造函数
RequestQueue() {
}
// 获取请求队列实例
public RequestQueue getQueueInstance() {
return new RequestQueue();
}
// 获取内部请求队列
public LinkedList<Integer> getInternalRequests() {
return internalRequests;
}
// 设置内部请求队列
public void setInternalRequests(LinkedList<Integer> internalRequests) {
this.internalRequests = internalRequests;
}
// 获取外部请求队列
public LinkedList<ExternalRequest> getExternalRequests() {
return externalRequests;
}
// 设置外部请求队列
public void setExternalRequests(LinkedList<ExternalRequest> externalRequests) {
this.externalRequests = externalRequests;
}
// 添加内部请求到队列
public void addInternalRequests(int floor) {
internalRequests.add(floor);
}
// 添加外部请求到队列
public void addExternalRequest(int floor, Direction direction) {
ExternalRequest externalRequest = new ExternalRequest(floor, direction);
externalRequests.add(externalRequest);
}
}
// 枚举类,定义电梯的运行方向
enum Direction {
UP, DOWN, IDLE
}
// 枚举类,定义电梯的状态
enum State {
MOVING, STOPPED
}
第二次代码类图:

第二次代码分析图:
SM代码分析图:

文字分析:
代码规模指标
- Lines(386 行):代码总行数为 386 ,反映代码文件在文本层面的长度。
- Statements(234 条):可执行语句总数 234 条 ,相比行数,该指标更直接体现代码的执行量。平均下来每行约有 0.61 条语句,说明代码并非特别紧凑。
- Classes and Interfaces(7 个):代码中定义了 7 个类或接口,表明代码通过多个不同的类型结构来组织功能,具备一定的模块化程度。
- Methods per Class(5.29):平均每个类包含 5.29 个方法,说明类的功能相对多样,内聚性尚可,但也需关注方法间的逻辑关联是否合理。
- Average Statements per Method(4.76):每个方法平均有 4.76 条语句,方法规模整体不算大,从侧面反映代码可能在方法拆分上较为细致。
代码结构指标 - Percent Branch Statements(26.9%):分支语句(如if、switch等)占总语句的比例为 26.9% ,说明代码中存在一定程度的逻辑分支判断,但并非过度复杂,不过仍可进一步审视分支逻辑的合理性。
- Method Call Statements(140 条):方法调用语句达 140 条 ,体现代码中方法间交互频繁,合理的方法调用可提升代码复用性和模块化程度,但也可能存在调用关系混乱的风险。
- Line Number of Most Complex Method(153 行):最复杂方法位于第 153 行,可快速定位到该位置,针对性分析和优化。
- Name of Most Complex Method(
Controller.process()):明确最复杂方法是Controller类中的process方法,需重点关注该方法,考虑拆分或简化逻辑。 - Maximum Complexity(26):最大复杂度为 26 ,圈复杂度较高,意味着该方法逻辑判断和分支情况复杂,理解、测试和维护难度大。
- Line Number of Deepest Block(97 行):最深代码块在第 97 行,此处代码嵌套层次深,可能导致可读性和可维护性变差。
- Maximum Block Depth(7):最大代码块深度为 7 层 ,嵌套过深,可能存在代码结构不合理的情况,可通过提取子方法、优化逻辑结构等方式改善。
- Average Block Depth(2.73):平均代码块深度 2.73 ,说明整体代码存在一定程度的嵌套,有优化空间。
- Average Complexity(3.03):平均复杂度 3.03 ,结合最大复杂度和深度指标,部分方法复杂度较高,需进行优化以降低整体复杂度。
心得:
第二次题目是第一次的迭代,就是将一个类分成多个类另外将连续的相同请求保留一个,如果第一次就已经将方法分的很细致了,那就很简单就能将各类分好,可是我第一次的代码简直不忍直视,处理请求的方法耦合性太高🙄,以至于分的难度很大,所以我打算将方法重写,并且放弃使用定长数组改用Linklist(长度可变,便于增删,能同时存储多个属性),这样处理起来更加方便,也不会浪费内存或越界,将类、方法都细分好后,代码的可维护性与扩展性变得更好了,看起来也更加舒服、易读。
第三次题目
点击查看代码
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.LinkedList;
/**
* 主程序类,负责输入处理和系统初始化
*/
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
// 读取电梯运行的最低和最高楼层
int lowestStair = input.nextInt();
int highestStair = input.nextInt();
// 结束标志
String endFlag = "end";
// 请求数组(最多存储100条请求,包含去重逻辑)
String[] requests = new String[100];
requests[0] = input.next(); // 初始化第一个请求
int i = 1;
// 循环读取请求,直到遇到"end"(不区分大小写)
while (!requests[i-1].equalsIgnoreCase(endFlag)) {
String currentRequest = input.next();
// 去重处理:当前请求与前一个不同时才存储
if (!requests[i-1].equals(currentRequest)) {
requests[i] = currentRequest;
i++;
}
}
// 创建电梯实例(初始化运行范围)
Elevator elevator = new Elevator(lowestStair, highestStair);
// 创建请求队列实例
RequestQueue requestQueue = new RequestQueue();
int j = 0;
// 解析请求数组,提取有效请求并加入队列(跳过最后的"end"标志)
for (; j < i - 1; j++) {
String requestStr = requests[j];
// 正则表达式匹配两种请求格式:
// 1. 外部请求格式:<起点楼层,终点楼层>
String regExExternal = "<(\\d+),(\\d+)>";
// 2. 内部请求格式:<目标楼层>
String regExInternal = "<(\\d+)>";
Pattern patternExternal = Pattern.compile(regExExternal);
Matcher matcherExternal = patternExternal.matcher(requestStr);
Pattern patternInternal = Pattern.compile(regExInternal);
Matcher matcherInternal = patternInternal.matcher(requestStr);
if (matcherExternal.find()) { // 匹配外部请求
int sourceFloor = Integer.parseInt(matcherExternal.group(1));
int destFloor = Integer.parseInt(matcherExternal.group(2));
// 验证楼层有效性
if (elevator.isValidFloor(sourceFloor) && elevator.isValidFloor(destFloor)) {
requestQueue.addExternalRequest(sourceFloor, destFloor);
}
} else if (matcherInternal.find()) { // 匹配内部请求
int destFloor = Integer.parseInt(matcherInternal.group(1));
if (elevator.isValidFloor(destFloor)) {
requestQueue.addInternalRequests(destFloor);
}
}
}
// 创建控制器并关联电梯和请求队列
Controller controller = new Controller(elevator, requestQueue);
controller.print(); // 打印初始状态
controller.processNextRequest(); // 开始处理请求
}
}
/**
* 电梯类,负责维护电梯状态和基础操作
*/
class Elevator {
private final int lowestStair; // 最低楼层(不可变)
private final int highestStair; // 最高楼层(不可变)
private int currentFloor; // 当前楼层
private Direction direction; // 运行方向
private State state; // 运行状态(移动/停止)
/**
* 构造方法,初始化电梯运行范围
* @param lowestStair 最低楼层
* @param highestStair 最高楼层
*/
public Elevator(int lowestStair, int highestStair) {
this.lowestStair = lowestStair;
this.highestStair = highestStair;
// 初始状态:1楼,向上,停止(注:代码中初始状态在Controller的print方法中显示为UP,可能存在设计不一致)
this.currentFloor = 1;
this.direction = Direction.UP;
this.state = State.STOPPED;
}
// 省略部分Getter/Setter的注释(功能明确)
/**
* 验证楼层是否在有效范围内
* @param floor 待验证楼层
* @return 有效返回true,否则false
*/
public boolean isValidFloor(int floor) {
return floor >= lowestStair && floor <= highestStair;
}
}
/**
* 控制器类,负责协调电梯和请求队列的交互
*/
class Controller {
private final Elevator elevator; // 关联的电梯实例
private final RequestQueue requestQueue; // 关联的请求队列实例
/**
* 构造方法,初始化关联对象
*/
public Controller(Elevator elevator, RequestQueue requestQueue) {
this.elevator = elevator;
this.requestQueue = requestQueue;
}
/**
* 打印电梯当前状态(初始状态固定为1楼向上)
*/
public void print() {
System.out.print("Current Floor: 1 Direction: UP\n");
}
/**
* 主处理循环:持续处理请求直到队列为空
*/
public void processNextRequest() {
while (!requestQueue.getInternalRequests().isEmpty() ||
!requestQueue.getExternalRequests().isEmpty()) { // 只要有请求就继续处理
if (!requestQueue.getInternalRequests().isEmpty() &&
!requestQueue.getExternalRequests().isEmpty()) { // 同时存在内外请求
int nextFloor = process(); // 确定下一个目标楼层
if (nextFloor == 0) { // 无同向请求,需要换向
turnDirection();
} else if (nextFloor == -1) { // 外部请求与当前方向相反
// 先处理完当前方向的内部请求再处理反向外部请求
while (true) {
if (shouldStop(requestQueue.getExternalRequests().get(0).getSourceFloor())) {
// 到达外部请求楼层,转为内部请求
requestQueue.addInternalRequests(requestQueue.getExternalRequests().get(0).getDestination());
requestQueue.getExternalRequests().remove();
removeRequest(elevator.getCurrentFloor()); // 移除已处理请求
openDoors(); // 开关门
break;
}
move(); // 移动电梯
}
turnDirection(); // 处理完后换向
} else { // 正常处理同向请求
while (true) {
if (shouldStop(nextFloor)) { // 到达目标楼层
removeRequest(elevator.getCurrentFloor());
openDoors();
break;
}
move(); // 移动电梯
}
}
} else if (!requestQueue.getInternalRequests().isEmpty()) { // 仅有内部请求
// 根据目标楼层设置运行方向
int delta = requestQueue.getInternalRequests().get(0) - elevator.getCurrentFloor();
elevator.setDirection(delta > 0 ? Direction.UP : Direction.DOWN);
while (true) {
if (shouldStop(requestQueue.getInternalRequests().get(0))) {
removeRequest(elevator.getCurrentFloor());
openDoors();
break;
}
move();
}
} else if (!requestQueue.getExternalRequests().isEmpty()) { // 仅有外部请求
// 根据起点楼层设置运行方向
int delta = requestQueue.getExternalRequests().get(0).getSourceFloor() - elevator.getCurrentFloor();
elevator.setDirection(delta > 0 ? Direction.UP : Direction.DOWN);
while (true) {
if (shouldStop(requestQueue.getExternalRequests().get(0).getSourceFloor())) {
// 到达起点楼层,转为内部请求
requestQueue.addInternalRequests(requestQueue.getExternalRequests().get(0).getDestination());
requestQueue.getExternalRequests().remove();
removeRequest(elevator.getCurrentFloor());
openDoors();
break;
}
move();
}
}
}
}
/**
* 决策下一个处理的请求楼层
* @return 目标楼层(0表示换向,-1表示反向外部请求)
*/
private int process() {
int flag = 0; // 默认返回0(换向标志)
// 提取当前内外请求的首个元素
Passenger firstExternal = requestQueue.getExternalRequests().get(0);
int firstInternal = requestQueue.getInternalRequests().get(0);
int currentFloor = elevator.getCurrentFloor();
// 计算楼层差
int externalDelta = firstExternal.getSourceFloor() - currentFloor;
int internalDelta = firstInternal - currentFloor;
if (elevator.getDirection() == Direction.UP) { // 向上运行时的逻辑
if (externalDelta >= 0 && internalDelta >= 0) { // 内外请求都在上方
if (externalDelta >= internalDelta) { // 内部请求更近
flag = firstInternal;
} else { // 外部请求更近且方向向上
if (firstExternal.getDirection() == Direction.UP) {
flag = firstExternal.getSourceFloor();
requestQueue.addInternalRequests(firstExternal.getDestination()); // 转为内部请求
} else { // 外部请求方向向下,优先处理内部
flag = firstInternal;
}
}
} else if (externalDelta > 0 && internalDelta < 0) { // 外部在上,内部在下
if (firstExternal.getDirection() == Direction.UP) { // 同向外部请求优先
flag = firstExternal.getSourceFloor();
requestQueue.addInternalRequests(firstExternal.getDestination());
} else { // 反向外部请求,标记为-1(后续处理)
flag = -1;
}
} else if (externalDelta < 0 && internalDelta > 0) { // 外部在下,内部在上
flag = firstInternal; // 优先处理内部请求
}
} else if (elevator.getDirection() == Direction.DOWN) { // 向下运行时的逻辑
if (externalDelta <= 0 && internalDelta <= 0) { // 内外请求都在下方
if (externalDelta <= internalDelta) { // 内部请求更近
flag = firstInternal;
} else { // 外部请求更近且方向向下
if (firstExternal.getDirection() == Direction.DOWN) {
flag = firstExternal.getSourceFloor();
requestQueue.addInternalRequests(firstExternal.getDestination());
} else { // 反向外部请求,优先处理内部
flag = firstInternal;
}
}
} else if (externalDelta < 0 && internalDelta > 0) { // 外部在下,内部在上
if (firstExternal.getDirection() == Direction.DOWN) { // 同向外部请求优先
flag = firstExternal.getSourceFloor();
requestQueue.addInternalRequests(firstExternal.getDestination());
} else { // 反向外部请求,标记为-1
flag = -1;
}
} else if (externalDelta > 0 && internalDelta < 0) { // 外部在上,内部在下
flag = firstInternal; // 优先处理内部请求
}
}
return flag;
}
/**
* 移动电梯并输出状态
*/
private void move() {
if (elevator.getDirection() == Direction.UP) {
elevator.setCurrentFloor(elevator.getCurrentFloor() + 1);
System.out.printf("Current Floor: %d Direction: UP\n", elevator.getCurrentFloor());
} else if (elevator.getDirection() == Direction.DOWN) {
elevator.setCurrentFloor(elevator.getCurrentFloor() - 1);
System.out.printf("Current Floor: %d Direction: DOWN\n", elevator.getCurrentFloor());
}
elevator.setState(State.MOVING); // 移动时更新状态
}
/**
* 判断是否需要停止(当前楼层等于目标楼层)
*/
private boolean shouldStop(int floor) {
return floor == elevator.getCurrentFloor();
}
/**
* 开关门操作模拟
*/
private void openDoors() {
System.out.printf("Open Door # Floor %d\nClose Door", elevator.getCurrentFloor());
if (!requestQueue.getInternalRequests().isEmpty() ||
!requestQueue.getExternalRequests().isEmpty()) {
System.out.print("\n"); // 若有剩余请求则换行
}
elevator.setState(State.STOPPED); // 停止时更新状态
}
/**
* 移除已处理的请求(内外请求分别处理)
* @param currentFloor 当前楼层
*/
private void removeRequest(int currentFloor) {
// 处理内部请求
if (!requestQueue.getInternalRequests().isEmpty() &&
requestQueue.getInternalRequests().get(0) == currentFloor) {
requestQueue.getInternalRequests().remove(0);
}
// 处理外部请求(仅移除同向的已到达起点请求)
if (!requestQueue.getExternalRequests().isEmpty() &&
requestQueue.getExternalRequests().get(0).getDirection() == elevator.getDirection() &&
requestQueue.getExternalRequests().get(0).getSourceFloor() == currentFloor) {
requestQueue.getExternalRequests().remove(0);
}
}
/**
* 切换电梯运行方向
*/
private void turnDirection() {
elevator.setDirection(elevator.getDirection() == Direction.UP ? Direction.DOWN : Direction.UP);
}
}
/**
* 请求队列类,管理内部和外部请求
*/
class RequestQueue {
private final LinkedList<Integer> internalRequests; // 内部请求(楼层列表)
private final LinkedList<Passenger> externalRequests; // 外部请求(乘客对象列表)
public RequestQueue() {
this.internalRequests = new LinkedList<>();
this.externalRequests = new LinkedList<>();
}
// 省略部分Getter/Setter的注释(功能明确)
/**
* 添加内部请求(目标楼层)
*/
public void addInternalRequests(int floor) {
internalRequests.add(floor);
}
/**
* 添加外部请求(起点和终点楼层)
*/
public void addExternalRequest(int sourceFloor, int destFloor) {
externalRequests.add(new Passenger(sourceFloor, destFloor));
}
}
/**
* 乘客类,表示外部请求
*/
class Passenger {
private final int sourceFloor; // 起点楼层(不可变)
private final int destination; // 终点楼层(不可变)
public Passenger(int sourceFloor, int destination) {
this.sourceFloor = sourceFloor;
this.destination = destination;
}
// 省略部分Getter的注释(功能明确)
/**
* 获取乘客请求的方向(根据起止楼层判断)
* @return 向上或向下
*/
public Direction getDirection() {
return destination - sourceFloor > 0 ? Direction.UP : Direction.DOWN;
}
}
/**
* 电梯状态枚举(移动/停止)
*/
enum State {
MOVING, STOPPED
}
/**
* 运行方向枚举(向上/向下/空闲)
*/
enum Direction {
UP, DOWN, IDLE
}
第三次电梯类图:

第三次电梯分析:
SM分析图:

文字分析:
结构与控制相关指标
- Percent Branch Statements:25.4% 。分支语句(如if - else、switch等)在整个代码语句中占比约四分之一。这说明代码中存在不少条件判断逻辑,意味着程序在运行时会根据不同条件走不同的执行路径,一定程度上反映了代码逻辑的复杂性。较多的分支语句可能导致代码的可读性和可维护性降低,因为需要考虑各种分支情况。
- Maximum Block Depth:7 ,Average Block Depth:2.77 。最大代码块深度为 7 ,意味着代码中存在嵌套层次达到 7 层的代码块(例如if - else、for、while等语句的多层嵌套),平均代码块深度为 2.77 。较深的代码块嵌套会使代码逻辑变得复杂,难以跟踪和理解程序的执行流程,也增加了调试的难度,并且容易出现逻辑错误。
方法与类相关指标 - Classes and Interfaces:7 个。说明该代码文件中定义了 7 个类或接口,表明代码的结构相对复杂,涉及多个不同的类型定义。较多的类和接口可能意味着代码在进行模块化设计,将不同功能封装在不同的类型中,但也可能存在类型划分不合理、职责不清晰的情况。
Methods per Class:6.00 ,即每个类平均有 6 个方法。这表示类的功能相对丰富,但也可能存在类的职责不够单一的问题。按照面向对象设计的单一职责原则,一个类应该尽量只负责一项单一的功能,如果方法过多,可能意味着类承担了过多不同类型的职责,这会影响代码的可维护性和可扩展性。 - Average Statements per Method:4.40 ,每个方法平均有 4.4 条语句。从这个数据看,方法的平均体量不算大,但结合整体代码的规模和复杂度,不能仅依据此数据判断方法的质量。有些方法可能虽然语句少,但逻辑复杂;或者存在方法功能过于简单、碎片化的情况。
- Line Number of Most Complex Method:205 ,对应方法为Controller.process() ,Maximum Complexity:26 ,Average Complexity:2.79 。Controller.process() 方法的行号为 205 ,复杂度达到 26 ,远高于平均复杂度 2.79 。方法复杂度高通常意味着方法内包含较多的逻辑判断、循环、嵌套结构等,理解、测试和维护这样的方法难度较大,可能需要花费更多的时间和精力去梳理其逻辑,排查潜在的问题。
- Method Call Statements:148 。代码中方法调用语句有 148 条,这体现了代码中方法之间的交互较为频繁。频繁的方法调用可能是代码进行模块化设计、功能复用的体现,但也可能存在方法调用层次过深、关系混乱的问题,使得代码的调用链路难以理清。
心得:
第三次迭代相对于第二次题目来说主要是需求发生了一定的变化,所以只要在第二次的基础上略微改动一下即可,这让我体会到了如果将单一职责原则、迪米特法则等运用得很好的话,在面对需求改变时可以节省大量时间😄。
踩坑心得:
1.在第一次代码编写完成后,测试样例是通过了,但结果是答案错误,由此可见此时我的算法还是存在一些问题的,但我找了许久没发现错处。直到上课时老师随便提了一下,提供了一个新的测试用例,我根据这个测试用例找到了错误点,那就是电梯开始运行时我只考虑了距离而没考虑外部请求与运行方向不同方向的情况。具体如下图:
给定一个测试用例:
1
20
< 3,DOWN>
<4>
<5>
END
改正前:
代码错误点:

测试结果:

改正后:
代码:

测试结果:

由此我学习到了自己能够设置测试样例的重要性,这是寻找缺陷的重要方法之一。
2.第二次代码编写完成后出现了非零返回的提示,这时候我们就应该想想什么情况下会出现非零返回这种情况,因为在第一次我没有出现过这种情况,而第二次与第一次的一大不同点在于,第一次我用的是定长数组,第二次我用的是集合框架中的LinkList类,而在使用LinkList时什么情况下又会出现非零返回呢?答案是在当LinkList的列队为空时还调用了其get()方法,但我看了看我的代码已经提前判定其是否为空的情况为什么还是错的,原来我错误点在这:

这里我先用了get()方法再来判定其是否为空,这样显然是不可取的,而应该直接使用isEmpty()方法来判定一个LinkList列队是否为空。
3.第三次代码编写完后出现了部分错误,这同样也能显示出我应该是存在着算法方面的缺陷,这时也可设置测试用例来定位缺陷,可第三次代码我将方法进行了一定的细分,而不像第一次代码那样一个方法将大部分功能都实现了,方法的调用关系变得更加复杂,所以此时即使设置好了测试用例,也很难找到错误点在哪?这时就可以测试样例配合单步调试一起使用。
这里设置一个测试yli:
1
20
< 3,2>
<4,5>
<6,1>
<10>
end
修改前的运行结果出现以下情况:

六楼连续停了两次
单步调试:

在只剩下外部请求,电梯运行方向与目标方向不同时,外部请求的请求位置已经处理过了而没被正确移除列队。
所以由此我又学会了另一个寻找缺陷的重要法宝之一——单步调试。
改进建议:
1.编码第一次电梯题目时,由于对面向对象(一个方法就实现一个行为即可)的理解不太深刻,用一个方法实现了多个功能,而导致修改难度增大,所以在编码前应对每一个行为一一细分出来。(单一职责原则)
2.写代码时有时目的性太强,就为了通过某个测试用例而加一些不太需要的方法和属性,而没有普适性,所以应该将给出的一个测试用例当做一个提示而不是最终目标。👌
总结
通过这三次题目集,我收获了许多,其一就是上文提到过的正则表达式(处理String类的利器),这是在C语言中从未有过的体验,其带来了极大的便利👍👍👍;其次呢我对于面向对象的理解变得更加深刻了,一个类该有哪些属性,该有哪些方法,这是编码前需要考虑的;再则学到了LinkList,枚举体等的用法,让我体会到了Java的魅力。最后呢,我所要说的就是Java的设计模式非常重要,这是写出优质代码的基础(七大原则[2])
- S-LOOK:最短LOOK算法,用于处理磁头位于远端请求之间的情况,通过判断选择先处理哪个方向的请求以最小化寻道距离。
单一职责原则(SRP) 一个类只负责一项职责 提高代码可读性和可维护性 UserDataStorage类负责用户信息存储,UserValidator类负责用户信息验证
开闭原则(OCP) 对扩展开放,对修改关闭 增加新功能时通过扩展而非修改已有代码 通过实现Shape接口扩展不同图形类
里氏替换原则(LSP) 子类能替换父类且不影响系统正确性 保证子类可完全替代父类功能 Sparrow类可替代Bird类
依赖倒置原则(DIP) 高层和低层模块都依赖抽象,面向接口编程 降低模块间耦合度 SwitchController依赖Switch接口
接口隔离原则(ISP) 客户端不依赖不需要的接口,拆大接口为小接口 避免依赖不必要的方法 将Worker接口拆分为Workable和Eatable接口
迪米特法则(LoD) 对象对其他对象了解最少,只和直接朋友通信 降低类间耦合度 Student通过自身方法获取School信息
合成复用原则(CRP) 优先用组合 / 聚合而非继承实现复用 提高系统灵活性和可维护性 DataAccess类组合DatabaseConnection类实现功能 ↩︎
浙公网安备 33010602011771号