第一次blog作业
一.前言
最初着手开发电梯调度程序时,我充满了信心,觉得只要实现基本的楼层请求处理和电梯运行模拟就大功告成。于是,我快速搭建起了基础框架:用 Request 类记录每一个电梯请求的楼层与方向;封装了一个简易的 RequestQueue 类来管理请求队列;Elevator 类则承担起电梯运行的核心逻辑。然而,当我真正开始编写电梯调度核心方法,比如 getNextRequest 和 moveToFloor 时,才发现事情远没有想象中简单。复杂的逻辑判断和嵌套循环让代码变得异常臃肿,可读性几乎为零。那一刻,我深刻体会到,编程不能只追求功能的实现,代码的质量和可维护性同样至关重要。在一次又一次的修改,优化后我才得到了最终的答案。
二.设计过程
以第6次作业为例
点击查看代码
import java.util.*;
class Request {
int floor;
String direction;
public Request(int floor, String direction) {
this.floor = floor;
this.direction = direction;
}
}
class queue{
Queue<Request> q=new LinkedList<Request>();
void add(Request r){
q.add(r);
}
Request peek(){
return q.peek();
}
Request poll(){
return q.poll();
}
boolean isEmpty(){
return q.isEmpty();
}
}
class Control{
Elevator elevator;
int minFloor;
int maxFloor;
public Control(Elevator elevator, int minFloor, int maxFloor) {
this.elevator = elevator;
this.minFloor = minFloor;
this.maxFloor = maxFloor;
}
void add(String s)
{
if (s.contains(",")) {
String[] parts = s.split(",");
int floor = Integer.parseInt(parts[0]);
String direction = parts[1];
if(floor<=maxFloor&&floor>=minFloor)
elevator.getoq().add(new Request(floor, direction));
} else {
int floor = Integer.parseInt(s);
if(floor<=maxFloor&&floor>=minFloor)
elevator.getiq().add(new Request(floor, null));
}
}
}
class Elevator {
private int currentFloor = 1;
private String status = "Stopped";
private String direction = "UP";
queue iq=new queue();
queue oq=new queue();
void setfloor(int floor)
{
this.currentFloor=floor;
}
queue getiq()
{
return iq;
}
queue getoq()
{
return oq;
}
public void run() {
System.out.println("Current Floor: " + currentFloor + " Direction: " + direction);
while (!iq.isEmpty() || !oq.isEmpty()) {
Request nextRequest = getNextRequest();
if (nextRequest == null) {
break;
}
moveToFloor(nextRequest);
}
}
private Request getNextRequest() {
Request internal = iq.peek();
Request external = oq.peek();
if (internal == null && external == null) {
return null;
}
if(direction.equals("UP"))
{
if(internal == null)
{
if(external.floor > currentFloor)
{
return external;
}
else
{
direction = "DOWN";
return external;
}
}
if(external == null)
{
if(internal.floor > currentFloor)
{
return internal;
}
else
{
direction = "DOWN";
return internal;
}
}
if(internal.floor>currentFloor||external.floor>currentFloor&&external.direction.equals("UP"))
{
if(external.direction.equals("DOWN")||external.floor<currentFloor)return internal;
if(internal.floor<currentFloor)return external;
if (Math.abs(internal.floor - currentFloor) < Math.abs(external.floor - currentFloor)) {
return internal;
} else {
return external;
}
}
else
{
direction ="DOWN";
if(external.direction.equals("DOWN")&&external.floor>currentFloor)
{
direction="UP";
return external;
}
if(external.direction.equals("UP"))
{
return internal;
}
if (Math.abs(internal.floor - currentFloor) < Math.abs(external.floor - currentFloor)) {
return internal;
} else {
return external;
}
}
}
if(direction.equals("DOWN"))
{
if(external==null) {
if (internal.floor > currentFloor) {
direction = "UP";
return internal;
} else {
return internal;
}
}
if(internal==null) {
if (external.floor > currentFloor) {
direction = "UP";
return external;
} else {
return external;
}
}
if(internal.floor<currentFloor||external.floor<currentFloor&&external.direction.equals("DOWN"))
{
if(external.direction.equals("UP"))return internal;
if(internal.floor>currentFloor)return external;
if (Math.abs(internal.floor - currentFloor) < Math.abs(external.floor - currentFloor)) {
return internal;
} else {
return external;
}
}
else
{
direction ="UP";
if(external.direction.equals("UP")&&external.floor<currentFloor)
{
direction="DOWN";
return external;
}
if (Math.abs(internal.floor - currentFloor) < Math.abs(external.floor - currentFloor)) {
return internal;
}
else
{
return external;
}
}
}
return null;
}
private void moveToFloor(Request request) {
int nowFloor =currentFloor;
boolean flag=false;
if(direction.equals("UP"))
{
while (request.floor > currentFloor) {
flag=true;
if(currentFloor != nowFloor)
System.out.println("Current Floor: " + currentFloor + " Direction: " + direction);
currentFloor++;
}
if(flag==true)
System.out.println("Current Floor: " + currentFloor + " Direction: " + direction);
System.out.println("Open Door # Floor " + currentFloor);
System.out.println("Close Door");
while (!iq.isEmpty() && iq.peek().floor == request.floor) {
iq.poll();
}
while(!oq.isEmpty() && oq.peek().floor == request.floor&&oq.peek().direction==request.direction) {
oq.poll();
}
if(request.direction!=null&&request.direction.equals("DOWN"))direction="DOWN";
}
else
{
boolean flag1=false;
while (request.floor < currentFloor) {
flag1=true;
if(currentFloor != nowFloor)
System.out.println("Current Floor: " + currentFloor + " Direction: " + direction);
currentFloor--;
}
if(flag1==true)
System.out.println("Current Floor: " + currentFloor + " Direction: " + direction);
System.out.println("Open Door # Floor " + currentFloor);
System.out.println("Close Door");
while (!iq.isEmpty() && iq.peek().floor == request.floor) {
iq.poll();
}
while(!oq.isEmpty() && oq.peek().floor == request.floor&&oq.peek().direction==request.direction) {
oq.poll();
}
if(request.direction!=null&&request.direction.equals("UP"))direction="UP";
}
}
}
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();
Elevator elevator = new Elevator();
elevator.setfloor(minFloor);
Control control = new Control(elevator, minFloor, maxFloor);
String input,next="";
while (!(input = scanner.nextLine()).equalsIgnoreCase("end")) {
if(input.equals(next))continue;
next=input;
input = input.replace("<", "").replace(">", "");
control.add(input);
}
elevator.run();
}
}
类的设计分析
类图
1. Request 类
功能:该类用于表示一个电梯请求,包含请求的楼层 floor 以及请求的方向 direction。
优点:将请求的相关信息封装在一起,提升了代码的可读性与可维护性,使其他类能方便地处理请求对象。
不足:缺乏对输入参数的验证,例如 direction 可能输入无效值。
2. queue 类
功能:对 Queue
优点:封装了队列操作,让其他类能更便捷地使用队列,降低了代码的耦合度。
不足:类名 queue 不符合 Java 的命名规范,应使用大写字母开头的驼峰命名法,如 RequestQueue。
3. Control 类
功能:该类负责接收用户输入的请求,并依据输入格式将请求添加到电梯的内部队列或外部队列。
优点:将请求的处理逻辑集中在一个类中,增强了代码的可维护性。
不足:add 方法的逻辑较为复杂,包含了较多的条件判断,可考虑将部分逻辑提取成独立的方法以提高代码的可读性。
4. Elevator 类
功能:该类是核心类,模拟电梯的运行。包含电梯的当前楼层、状态、方向,以及内部请求队列和外部请求队列。提供了运行电梯、获取下一个请求和移动到指定楼层的方法。
优点:
封装了电梯的各种属性和行为,使代码结构清晰。
getNextRequest 方法根据电梯的当前方向和请求情况,选择合适的下一个请求,逻辑相对完整。
不足:
getNextRequest 方法和 moveToFloor 方法的逻辑非常复杂,包含大量的条件判断和嵌套,代码的可读性和可维护性较差。
部分代码存在重复,如在 moveToFloor 方法中,处理电梯上升和下降的逻辑有很多相似之处,可以考虑提取公共部分。
5. Main 类
功能:该类是程序的入口点,负责读取用户输入,创建电梯和控制器对象,并启动电梯运行。
优点:将程序的启动逻辑集中在一个类中,使代码结构清晰。
不足:输入处理部分的代码较为繁琐,例如使用 scanner.nextLine() 和 scanner.nextInt() 混合读取输入,容易引发输入异常
sourcemonitor解析代码
其中sourcemonitor图中解释如下:
行数:261 行
语句数:177 条
分支语句百分比:31.6% ,意味着约四分之一语句是分支逻辑(if-else)
方法调用语句数:70 条
注释行百分比:0.0% ,即代码无注释
类和接口数量:5 个
每个类的平均方法数:2.80 个
每个方法的平均语句数):10.43条
最复杂方法的行号:77 行,方法为 Elevator.getNextTargetFloor()
最大复杂度为43,方法也是Elevator.getNextTargetFloor()
最深代码块行号:91 行
最大代码块深度为5
平均代码块深度:2.93
平均复杂度:6.21
3.踩坑心得
思考代码逻辑时不严谨,导致最后写完的代码不能完全正确,并且想要修改也异常困难,我开始逐行的去查找错误最后并未发现问题,反而染费了大量的时间,最后重新写一遍才写对。
字母大小写要明确,不然最后的bug会有很多,写完IDEA报错几十个点会吓到人的
在处理请求队列时,没有及时移除已处理的请求,导致电梯在同一楼层反复开门关门。例如,moveToFloor方法中,没有从iq或oq队列中删除已到达楼层的请求,导致代码最开始一直死循环
解决方法:在请求处理完成后,使用poll()方法从队列中移除已处理的请求。
4.改进建议
设计逻辑严谨,排版代码的类有序且美观,使代码的可读性更上一层楼
题目并不难,但要做到完美确很难,在写题目的过程中发展自己的多方面
如代码的面向对象性,可读性,然后做到代码少bug。
5.总结
经过这几次的电梯调度程序,使我加深了对java的理解,并初次体验了java的风格和魅力,虽然过程中遇到的新难题也很多,但也一一解决了
但java学习才刚刚开始,应该不骄不躁继续努力
总的的来说题目很好,过程也很好