PTA三次电梯程序设计作业心得
前言
在本次电梯实验中,我学到了很多,比如:
- 熟悉了Java基本语法;
- 初步建立起了面向对象的设计思路;
- 熟悉了类的设计;
- 学会了什么是队列,什么是出队入队;
- 熟悉了正则表达式的运用。
第一次单部电梯调度程序设计
作业要求
设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。
请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求),具体运行规则详见输入输出样例。
实现方式
通过定义枚举类型、类和方法,实现一个简单的电梯模拟系统。
代码分析

此次程序设计的代码复杂度过高,主要是因为写代码时使用了过多的while以及if语句。
错误原因分析
此次程序输出结果为运行超时,对于这个结果,我思考出以下几种原因:
- 对于题目的要求没有理解透彻,导致代码并不能完成想要的功能。
- 对于队列的运用并不清楚,在很长一段时间内都无法正确解决出队入队问题。
- 对于此代码该如何运行没有形成一个完整且清楚的逻辑,导致代码复杂度高,逻辑混乱。
- 代码整体缺乏模块化设计,各个功能之间的耦合度较高,导致很难找到问题的根源,一直无法解决运行超时的问题。例如在判断是否还有上行或下行请求的方法中,所有的判断功能包含在同一方法里面,但此方法其实可以分为多个,合为一个代码会包含大量的逻辑,使代码行数较多,难以进行逻辑正确性的判断,不利于代码的修改和测试。
第二次单部电梯调度程序设计
作业要求
对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客请求类、队列类以及控制类。
电梯运行规则与前阶段单类设计相同,但要处理如下情况:
乘客请求楼层数有误,具体为高于最高楼层数或低于最低楼层数,处理方法:程序自动忽略此类输入,继续执行
乘客请求不合理,具体为输入时出现连续的相同请求,例如<3><3><3>或者<5,DOWN><5,DOWN>,处理方法:程序自动忽略相同的多余输入,继续执行,例如<3><3><3>过滤为<3>
实现方式
通过定义枚举类型、设计多个类和相应的方法,实现了一个简单的电梯控制系统,能够通过接受用户输入的请求控制电梯的运行和停靠,并自动修改用户输入。
类图

代码分析

由于此次程序设计进行了类和方法的分类,代码复杂度较上次代码减小了不少,但代码中方法复杂度差异较大,有些方法非常简洁,有些则依旧运用了大量while与if语句。
错误原因分析
此次程序输出结果依旧为运行超时,对于这个结果,我思考出以下几种原因:
- 对于题目的要求还是没有理解透彻,需要多花时间理清楚题目的需求以及如何实现。
- 解决了出队入队的问题,但是由于题目新增的对于输入重复的需求,对于正则表达式的运用需要进行修改。
- 逻辑方面依旧不够清晰,各个方法的功能以及各方法间的调用关系依旧存在问题,如在实现查找下一个楼层的方法时,在判断下一个楼层的同时,由于逻辑的不清晰,将多个功能放在一个方法中实现,除此之外,由于逻辑的不清晰,即使写出了各个方法,但无法正确的将它们连接起来,还是无法实现所需功能,需要进一步明确各个方法的功能以及各方法间的调用关系,找到导致运行超时的源头。
第三次单部电梯调度程序设计
作业要求
对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客类、队列类以及控制类。
电梯运行规则与前阶段相同,但有如下变动情况:
乘客请求输入变动情况:外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)
实现方式
通过用户输入的相关信息,解析并创建对应对象,把乘客请求加入队列,按既定逻辑处理请求,实现电梯对乘客请求的响应和运行控制。
类图

代码分析

代码复杂程度不高,但还是存在方法复杂度差异较大的问题。
已改正的错误
在本次程序设计中,我花大量时间重新思考的电梯的运行流程以及该如何实现,通过画流程图并不断修改的方式确定了程序的逻辑、各个方法应该实现的功能以及方法间该如何调用,最后终于获得了正确的结果。
以下是最终代码的类设计以及各个方法所实现的功能:
Passenger 类
功能:用于表示乘坐电梯的乘客相关信息,包括乘客的出发楼层、目的楼层以及获取行程方向等功能。
具体功能:
- 提供了三个构造函数,一个无参构造函数用于默认初始化;一个接收出发楼层和目的楼层参数的构造函数,用于完整地初始化乘客的行程信息;还有一个只接收目的楼层参数的构造函数,可用于某些特定场景下对乘客信息的初始化。
- 通过 getDestinationFloor、getSourceFloor 方法分别获取乘客的目的楼层和出发楼层信息;通过 setDestinationFloor 和 setSourceFloor 方法可以设置对应的楼层信息。
- getDirection 方法根据乘客的出发楼层和目的楼层的大小关系,来确定乘客的行程方向,返回值为 Direction 枚举类型(包含 UP、DOWN、IDLE 三个值),分别对应向上、向下和无行程(出发楼层和目的楼层相同)的情况。
Elevator 类
功能:代表电梯实体,用于管理电梯自身的状态信息,如当前楼层、运行方向、运行状态以及电梯可运行的楼层范围等,提供了相应的属性获取和设置方法,还有判断楼层是否有效的方法。
具体功能:
- 接收电梯可运行的最低楼层和最高楼层参数,在构造时初始化电梯的当前楼层为最低楼层,运行方向为 IDLE(空闲),运行状态为 STOPPED(停止)。
- getElevatorInstance 方法可以根据传入的最低楼层和最高楼层参数创建并返回一个新的电梯实例。
- 通过 getCurrentFloor、getDirection、getState、getMaxFloor、getMinFloor 这些方法分别获取电梯当前所在楼层、运行方向、运行状态以及可运行的最大、最小楼层信息;通过对应的 set 方法(如setCurrentFloor、setDirection、setState 等)可以对这些属性进行设置。
- sValidFloor 方法用于判断传入的楼层参数是否在电梯可运行的楼层范围内,若楼层小于最低楼层或者大于最高楼层则返回 false,在范围内则返回 true。
Controller 类
功能:作为电梯控制系统的核心类,负责协调电梯和请求队列之间的交互,实现处理乘客请求、确定电梯运行方向、控制电梯移动、判断电梯停靠以及管理请求队列等一系列功能。
具体功能:
- 提供了无参构造函数以及接收电梯实例和请求队列实例的构造函数,用于初始化控制器对象与对应的电梯和请求队列关联。
- 通过 getElevator 和 getQueue 方法分别获取控制器关联的电梯对象和请求队列对象;通过对应的 set 方法(setElevator、setQueue)设置关联的电梯和请求队列。
- processRequests 方法是处理乘客请求的核心流程控制方法,它首先将电梯设置到最低楼层,然后确定电梯初始运行方向,接着在请求队列不为空的情况下,不断循环确定方向、移动电梯来依次处理各个请求。
- determineDirection 方法根据电梯当前楼层和下一个目标楼层的比较结果,来设置电梯的运行方向(向上、向下或空闲)。
- move 方法控制电梯按照当前设定的方向移动,直到到达下一个目标楼层,在移动过程中会输出当前楼层和运行方向信息,到达目标楼层后调用 openDoors 方法。
- shouldStop 方法根据电梯当前的运行方向是否为空闲来判断电梯是否需要在当前楼层停靠,若方向为空闲则返回 true,否则返回 false。
- getNextFloor 方法用于查找电梯接下来要前往的目标楼层,它根据内部请求队列、外部请求队列的不同情况(是否为空等)以及各个请求与电梯当前状态的关系,综合判断并返回下一个合适的目标楼层;若请求队列都为空则返回 null。
- getClosest 方法接收两个楼层参数,通过比较它们与电梯当前楼层的距离远近,返回距离更近的那个楼层。
- openDoors 方法模拟电梯开门的操作,输出开门和关门的提示信息,并且调用 removeRequests 方法来处理已到达楼层的请求。
- removeRequests 方法用于从请求队列中移除已处理的请求,对于内部请求,若队列头的乘客目的楼层与当前楼层一致则移除该请求;对于外部请求,若队列头的乘客出发楼层与当前楼层一致,则移除该外部请求,并将对应的乘客以当前楼层为出发楼层、原目的楼层不变的形式添加到内部请求队列末尾。
RequestQueue 类
功能:管理乘客的请求队列,将乘客请求分为内部请求和外部请求分别存储,并提供相应的队列操作方法,如添加请求、获取请求队列等。
具体功能:
- 定义了两个 LinkedList 类型的属性,internalRequest 用于存储内部乘客请求(即已经在电梯内的乘客的目的楼层请求),externalRequest 用于存储外部乘客请求(在电梯外等待乘坐电梯的乘客的出发楼层和目的楼层请求)。
- 通过 getInternalRequest 和 getExternalRequest 方法分别获取内部请求队列和外部请求队列的引用,方便外部对队列进行操作;通过对应的 set 方法(setInternalRequest、setExternalRequest)可以设置这两个请求队列。
- addInternalRequest 方法用于向内部请求队列中添加一个乘客请求(通过传入 Passenger 对象);addExternalRequest 方法用于向外部请求队列添加乘客请求。
Main 类
功能:负责读取用户输入的电梯相关信息(如最低楼层、最高楼层以及乘客请求等),创建电梯、请求队列和控制器对象,并将乘客请求添加到相应队列,最后调用控制器的请求处理方法来模拟电梯运行处理请求的整个过程。
具体功能:
- 通过 Scanner 读取用户输入的字符串信息,将输入的电梯最低楼层、最高楼层以及一系列乘客请求信息存储到 ArrayList 中,对输入的格式进行简单验证(如外部请求、内部请求格式是否匹配相应正则表达式),不符合格式要求的会输出提示信息。
- 根据读取的最低楼层和最高楼层创建电梯对象,创建请求队列对象以及控制器对象,并将电梯对象和请求队列对象关联到控制器上。
- 将用户输入的符合格式的乘客请求分别添加到对应的内部请求队列或外部请求队列中,最后调用控制器的 processRequests 方法来启动电梯处理请求的流程模拟。
需要继续改善的地方
除此之外,以下是几个我觉得还需要继续改进的地方:
- 由于对于类间关系的调用还不熟练,程序的耦合性比较强,依赖注入方法比较单一,这方面可以进行优化。
- 个人总是忘记添加代码注释,以后可以注重这方面的优化,使代码更具可读性。
采坑心得
在这三次的程序设计过后,我总结了以下几点需要注意:
- 在设计程序之前,对于所应用到的新的知识,如这次的出队入队、正则表达式等,需要提前熟悉,不要一知半解的去运用,否则会产生出现错误但无法分辨错误,无法修改的情况。
- 一定要完全理解程序实现功能的流程,以及对应的逻辑,用流程图来分析会事半功倍,一定要在完全搞定逻辑以后再开始写代码,否则很容易出现运行超时但无法解决的情况。
- 在完成代码,但代码有错误的时候,可以自己设计多个测试不同功能正确性测试点,并通过不同测试点来确定是哪个功能的实现逻辑出现了问题。(还可以通过IDEA来进行辅助调试找到问题)
总结
在本阶段三次题目集中,我学会了什么是队列,什么是出队入队,熟悉了正则表达式的运用,建立起了面向对象的设计思路,学会了如何通过调试代码以及设计测试用例来找到问题。
除此之外,对于类间关系的调用方式、各个方法间的逻辑处理、代码注释的添加等方面还需要进一步学习和研究。
浙公网安备 33010602011771号