5~7电梯题目总结
一.前言
在题目集5的电梯题目中,用到的知识点有如下这些枚举类型,类与对象的运用,getter和setter有封装,用了集合框架,还有条件if语句,for的循环语句,正则表达式,异常处理,题目中还要求要用LOOK算法,这是我第一次写这种有初步面向对象思想的思维编程,所以在刚接触的时候不知道怎么开始编程,但在后续老师给出一些提示后有了一些想法,渐渐能开始写代码。这个题目对于没接触过这样题目的我来说还是非常难的,题量也很大当时写了特别久。
在题目集6的电梯题目中,这次电梯运行过程增加了新的逻辑,需要对上一次的代码进行修改,虽然难度比上一次的高,但有上一次的代码基础,在这次代码的修改中,这次代码的修改相比上一次写代码,可以慢慢着手修改。
在题目集7的电梯题目中,这次电梯运行过程在输入格式上出现了变化,逻辑也发生了变化,这使得编程难度增加,考察了我们对于更加复杂的逻辑的处理能力。
二.设计与分析
电梯题目
设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求),具体运行规则详见输入输出样例。
(1)题目集5代码中的关键代码
用于找到下一层的代码
public int findNextTargetFloor() {
int targetFloor = -1000;
int firstInternal = 0;
int firstExternal = 0;
boolean hasInternal = false;
boolean hasExternal = false;
// 读取第一个内部请求
if (!in.isEmpty()) {
firstInternal = in.get(0);
hasInternal = true;
}
// 读取第一个外部请求
if (!out.isEmpty()) {
ExternalRequest firstExtReq = out.get(0);
if (firstExtReq.floor > currentFloor|| firstExtReq.floor < currentFloor) {
firstExternal = firstExtReq.floor;
hasExternal = true;
}
}
// 比较内外部请求
if (direction.equals("UP")) {
if (hasExternal && firstExternal > currentFloor&&out.get(0).direction.equals("UP")) {
targetFloor = firstExternal;
}
if (hasInternal && firstInternal > currentFloor) {
if (targetFloor == 0 || firstInternal < targetFloor) {
targetFloor = firstInternal;
}
}
} else {
if (hasExternal && firstExternal < currentFloor&&out.get(0).direction.equals("DOWN")) {
targetFloor = firstExternal;
}
if (hasInternal && firstInternal < currentFloor) {
if (targetFloor == 0 || firstInternal > targetFloor) {
targetFloor = firstInternal;
}
}
}
if(targetFloor == -1000){
targetFloor = DirectionSet(targetFloor);
}
return targetFloor;
}
下面的代码负责读取后的删除
private void handleRequests() {
boolean handled1= false;boolean handled2 = false;
// 处理外部请求
if (!out.isEmpty()) {
ExternalRequest firstExternalRequest = out.get(0);
if (firstExternalRequest.floor == currentFloor) {
if ((firstExternalRequest.direction.equals("UP") && direction.equals("UP")) ||
(firstExternalRequest.direction.equals("DOWN") && direction.equals("DOWN")) ||
direction.equals("IDLE")) {
out.remove(0);
handled1= true;
}
}
}
// 处理内部请求
if(!in.isEmpty()){
int firstInternalRequest = in.get(0);
if (firstInternalRequest == currentFloor) {
in.remove(0);
handled2 = true;
}
}
if (handled1 || handled2){
handleStop();
}
}
private void handleStop() {
System.out.println("Open Door # Floor " + currentFloor);
System.out.println("Close Door");
// direction = "IDLE";
}
}
class ExternalRequest {
public int floor; // 目标楼层
public String direction; // 目标楼层的方向
public ExternalRequest(int floor, String direction) {
this.floor = floor;
this.direction = direction;
}
}
handleRequests()这个方法在读取后进行删除的操作,我的代码删除的操作用remove进行删除。
(2)在题目集6的电梯题目中,要求在题目集5的电梯题目的基础上进行代码的迭代,新增了Controller类,在电梯运行过程中增加了新的逻辑,乘客请求楼层数有误,具体为高于最高楼层数或低于最低楼层数,处理方法:程序自动忽略此类输入。输入时出现连续的相同请求,处理方法:程序自动忽略相同的多余输入。
对于找到下一层的方法进行了逻辑上的升级
private int findNextTargetFloor() {
int targetFloor = -1000;
int firstInternal = 0;
int firstExternal = 0;
boolean hasInternal = false;
boolean hasExternal = false;
// 读取第一个内部请求
if (!queue.getInternalRequests().isEmpty()) {
firstInternal = queue.getInternalRequests().get(0);
hasInternal = true;
}
// 读取第一个外部请求
if (!queue.getExternalRequests().isEmpty()) {
ExternalRequest firstExtReq = queue.getExternalRequests().get(0);
firstExternal = firstExtReq.getFloor();
int mFloor1 = Math.abs(firstInternal - currentFloor);
int mFloor2 = Math.abs(firstExternal - currentFloor);
if (firstInternal > currentFloor && firstExternal > currentFloor) {
if(mFloor1 > mFloor2){
targetFloor = firstInternal;
}
} else {
if (firstInternal < currentFloor && firstInternal < currentFloor) {
if(mFloor1 < mFloor2){
targetFloor = firstExternal;
hasExternal = true;
}
} else {
if (mFloor1 < mFloor2 && !queue.getInternalRequests().isEmpty() && !queue.getExternalRequests().isEmpty()) {
targetFloor = firstInternal;
}
if (mFloor2 < mFloor1 && !queue.getExternalRequests().isEmpty() && !queue.getInternalRequests().isEmpty()) {
targetFloor = firstExternal;
hasExternal = true;
}
if (mFloor1 == mFloor2) {
if (firstExtReq.getFloor() > currentFloor && firstExtReq.getDirection().equals(Direction.DOWN)) {
firstExternal = firstExtReq.getFloor();
direction = "UP";
hasExternal = true;
}
if (firstExtReq.getFloor() < currentFloor && firstExtReq.getDirection().equals(Direction.UP)) {
firstExternal = firstExtReq.getFloor();
direction = "DOWN";
hasExternal = true;
}
if (firstExtReq.getFloor() > currentFloor || firstExtReq.getFloor() < currentFloor) {
firstExternal = firstExtReq.getFloor();
hasExternal = true;
}
}
}
}
}
// 比较内外部请求
if (direction.equals("UP")) {
if (hasExternal && firstExternal > currentFloor) {
targetFloor = firstExternal;
}
if (hasInternal && firstInternal > currentFloor) {
if (targetFloor == 0 || firstInternal < targetFloor) {
targetFloor = firstInternal;
}
}
} else {
if (hasExternal && firstExternal < currentFloor) {
targetFloor = firstExternal;
}
if (hasInternal && firstInternal < currentFloor) {
if (targetFloor == 0 || firstInternal > targetFloor) {
targetFloor = firstInternal;
}
}
}
if (targetFloor == -1000) {
targetFloor = DirectionSet(targetFloor);
}
return targetFloor;
}
通过对哪个楼层更近并且同方向进行进行逻辑上的优先级判断,影响目标楼层是几层。
(3)在题目集7的电梯题目中,要求在题目集6的电梯题目的基础上进行代码的迭代,新增了Passenger类,并且在输入的格式上进行了改变,外部请求的修改为<请求源楼层,请求目的楼层>
这次代码对于找到下个目标楼层的代码进行了简化
private int getNextFloor() {
if (queue.getInternalRequests().isEmpty() && queue.getExternalRequests().isEmpty()) {
return elevator.getCurrentFloor();
}
int internalFloor = Integer.MAX_VALUE;
int externalFloor = Integer.MAX_VALUE;
if (!queue.getInternalRequests().isEmpty()) {
internalFloor = queue.getInternalRequests().get(0).getDestinationFloor();
}
if (!queue.getExternalRequests().isEmpty()) {
externalFloor = queue.getExternalRequests().get(0).getSourceFloor();
}
if (elevator.getDirection() == Direction.UP) {
if (internalFloor > elevator.getCurrentFloor() && externalFloor > elevator.getCurrentFloor()) {
return Math.min(internalFloor, externalFloor);
} else if (internalFloor > elevator.getCurrentFloor()) {
return internalFloor;
} else if (externalFloor > elevator.getCurrentFloor()) {
return externalFloor;
}
} else if (elevator.getDirection() == Direction.DOWN) {
if (internalFloor < elevator.getCurrentFloor() && externalFloor < elevator.getCurrentFloor()) {
return Math.max(internalFloor, externalFloor);
} else if (internalFloor < elevator.getCurrentFloor()) {
return internalFloor;
} else if (externalFloor < elevator.getCurrentFloor()) {
return externalFloor;
}
}
return elevator.getCurrentFloor();
}
第七次题目集的类图:

下面是题目集7电梯代码分析后的图片与具体数据:

Metrics Details For File 'Main.java'
Parameter Value
========= =====
Project Directory D:\daima2\dianti4\src
Project Name ceshi1
Checkpoint Name Baseline
File Name Main.java
Lines 350
Statements 216
Percent Branch Statements 26.4
Method Call Statements 182
Percent Lines with Comments 0.0
Classes and Interfaces 7
Methods per Class 4.29
Average Statements per Method 5.47
Line Number of Most Complex Method 167
Name of Most Complex Method Controller.determineDirection()
Maximum Complexity 23
Line Number of Deepest Block 159
Maximum Block Depth 5
Average Block Depth 2.42
Average Complexity 3.50
Most Complex Methods in 5 Class(es): Complexity, Statements, Max Depth, Calls
Controller.Controller() 1, 2, 2, 0
Controller.determineDirection() 23, 35, 5, 54
Controller.getNextFloor() 15, 23, 4, 24
Controller.moveElevator() 5, 10, 4, 10
Controller.openDoors() 1, 2, 2, 2
Controller.processRequest() 5, 10, 5, 15
Controller.removeRequests() 6, 8, 4, 15
Controller.shouldStop() 6, 10, 4, 13
Elevator.addExternalRequest() 10, 7, 4, 16
Elevator.addInternalRequest() 5, 5, 3, 10
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.getState() 1, 1, 2, 0
Elevator.processRequests() 1, 2, 2, 1
Elevator.setCurrentFloor() 1, 1, 2, 0
Elevator.setDirection() 1, 1, 2, 0
Elevator.setState() 1, 1, 2, 0
Main.getFloor() 2, 4, 3, 3
Main.main() 6, 21, 4, 15
Passenger.getDestinationFloor() 1, 1, 2, 0
Passenger.getDirection() 3, 3, 3, 0
Passenger.getSourceFloor() 1, 1, 2, 0
Passenger.Passenger() 1, 2, 2, 0
RequestQueue.addExternalRequest() 1, 1, 2, 1
RequestQueue.addInternalRequest() 1, 1, 2, 1
RequestQueue.getExternalRequests() 1, 1, 2, 0
RequestQueue.getInternalRequests() 1, 1, 2, 0
Block Depth Statements
0 10
1 42
2 70
3 46
4 38
5 10
6 0
7 0
8 0
9+ 0
代码规模:
Lines 为 350 行,Statements 有 216 条,代码量不少。
结构复杂度:
Percent Branch Statements约四分之一的语句是分支语句 ,代码存在一定逻辑分支。
Method Call Statements 达 182 条,说明方法间调用频繁,代码模块间交互较多。
Classes and Interfaces 有 7 个 ,Methods per Class 平均 4.29 个,类和方法数量较多,代码结构相对复杂。
方法层面:
最复杂方法:Controller.determineDirection() 复杂度最高,Complexity 为 23 ,有 35 条 Statements ,Max Depth 达 5 ,方法调用次数 Calls 为 54 。说明该方法逻辑分支多、代码块嵌套深且频繁调用其他方法,理解和维护难度大。
较复杂方法:如 Controller.getNextFloor() 复杂度 15 ,Elevator.addExternalRequest() 复杂度 10 等,这些方法复杂度也较高,在代码逻辑和结构上有一定复杂性。
简单方法:像 Passenger.getDestinationFloor()、Elevator.getState() 等大量方法复杂度为 1 ,代码简单,功能单一,主要是简单的数据获取或赋值操作。
三.踩坑心得:
1.在第一次写电梯代码的过程中,经常会碰到有些知识不熟悉的情况,比如正则表达式运用不熟练,需要去把笔记翻出来再看看,还有在编译程序的过程中对于电梯移动的条件判断不够明确,有时候测试案例会出现移动到负楼层的情况,这些问题一开始不知道怎么解决在后续学习中对于这类条件判断的问题会更加注意。
2.在第二次迭代电梯代码的过程中,刚开始没有看清楚题目,导致运行案例时无法处理重复的请求,使得代码运行超时,通过这一次知道了要看清楚题目再进行代码的分析构思,最后才是修改写代码。
3.在第三次迭代电梯代码的过程中,遇到了较大的问题,没有处理好条件判断的优先级的问题,我的理解是有头部外部请求楼层大于内部请求楼层,如果是上升的状态则要先执行到最高的楼层后再改变方向,这个条件应该是最优先的然后再是,要判断外部请求是在向上的时候读取还是向下的时候才能读取到。在这次修改代码的过程中,明白了条件处理的优先级对于代码有着十分重要的作用。还遇到了到达某一楼层无法有效删除该层请求的问题,发现是由于电梯方向条件判断的问题所导致的无法对于请求有效的删除,所以在以后的编码过程中,还是要多注意条件的判断。
四.改进建议:
感觉这个电梯程序还是不符合我们日常生活中所坐的电梯移动时的逻辑,不够贴近生活,比如内部请求有3,8,4,外部请求为<5,9>,内部请求中的3个请求可能是一起出现的,所以应该上升到3楼后,到四楼也会停,但是现在设计的电梯程序在最开始上升时并不会在4楼停而是会执行外部请求到达5楼。但是这样感觉代码对于现在来说比较难实现。
五.总结:
通过三次电梯的题目集练习,我从一开始不怎么会用Java写代码到现在可以写出一些代码来,虽然过程很难,但是写出来后还是很有成就感的。在写代码的过程中也感觉到java程序与c语言程序存在挺多不同的地方,我觉得我还是要在java继承上多花时间学习一会,感觉这个是java与c语言不同的关键,感觉有了继承程序写起来就会很方便,特别是联系起生活中的图书管理,超市等和我们生活密切相关的系统时,更能感觉到它的方便。希望老师在后面的题目中能够将题目中的逻辑描述的更清楚一些,有时候感觉逻辑都是从老师给的案例中推理出来的,有时候不够准确会导致测试点错误,案例也可以多给几个,有时候出现案例都对了题目还没对的情况。
浙公网安备 33010602011771号