面向对象程序设计(Java)题目集5~7中电梯设计总结BLOG
一.前言
在最近的PTA编程作业中,题目集5~7中有关电梯程序设计的题目,以电梯调度系统为核心载体,通过三次逐步完成,逐步引导我们从单一电梯的基础行为过度到更复杂场景的场景,以及深化了我在编程过程中对代码封装,复用性,单一职责原则等意识原则的更深理解。三次题目集不仅是对编码能力的训练,对方法设计思维和类职责分配的考验的要求也有所加深,对方法设计的复用,合适程度相对之前进行过的代码设计都有了更加高的要求。
题目集所涉及知识点:
这三次题目集都涉及并主要考察了Java面对对象程序设计中有关类的设计构造与状态管理,强调了对方法复用性以及单一职责设计的重要性,同时在电梯程序的迭代设计中,还涉及到了正则表达式在输入数据时的运用,以及在处理队列数据时所使用到的LOOK算法设计,考察到了正则表达式的运用以及在处理数据时对算法的选择以及使用,对学生的编程能力和意识都有考察。
题目集题量感受:
题目集的题目数量总体上较少,但具体内容并没有因为题量较少而导致考察的内容不足,很好地通过少量而精的题目对学生的知识点以及方法对象的设计进行了多方面的检测。完成繁琐度不会太多,也不会过于轻松。能够让我较好地完成我对于面向对象设计知识的练习。
题目集难度感受与建议:
相对于前几次的题目集,5~7三次题目集的难度明显有所上升,尤其是涉及到电梯运行的三道编程题目,从第一次到第三次迭代题目难度逐步上升,题目长度和理解难度都显著上升,所考察的部分也越发深入,对学生能力的检测产生了一定的门槛,考验了更高的算法设计和方法构造以及对数据的处理能力。
二.设计与分析
在三次题目集中,有关电梯设计的三道迭代题目为主要核心以及关键考察内容,在此对其专门进行分析
第一次电梯题目设计与分析(题目集5)
题目要求
编程实现电梯程序,对于乘客的两种输入形式,分为接收外部请求和内部请求,然后再分别添加到内外两个请求队列中去。根据请求队列以及电梯调度算法来确定电梯的运行逻辑,请求处理后移除。最后打印出电梯每次运行的楼层和状态。
程序设计
SourceMontor报表
SourceMontor复杂度表
程序设计分析
MAIN类:作为代码的主要框架,通过正则表达式完成外部数据信息的接收,并根据接受的信息处理成Elevator类所能接受的请求,同时将请求置入Elevator类的内部与外部请求队列中。
Elevator类:定义电梯方向与运行状态的枚举,构造出内外请求队列,同时构造出请求队列所需的楼层,方向,运行状态等对象,以及负责通过队列信息完成对电梯运行的逻辑处理,同时负责电梯状态的打印。
枚举:Direction {UP,STOP,DOWN}(电梯方向);Status {MOVING,STOPPED}(电梯运行状态)
通过用枚举来定义电梯上行下行的方向,以及停止和运动运行的状态,保证了一定的高可读性及功能扩展性,有效提升代码健壮性与可维护性,并且保证了运动方向和运动状态对象的安全性。
问题分析:
从SourceMontor报表,SourceMontor复杂度表,PowerDesigner类图来分析,我们能够清晰地发现代码存在以下问题。
主要方法都集中于单个类中:如报表图和类图所示,通过分析可以看出代码的嵌套过深,类数过少的问题,因为第一次设计此类程序,将各种不同功能的方法都放入了一个类中,整个电梯程序的运行依托于一个Elevator类,整体上较为粗糙,难以维护,且复杂度过高,超过了一般java代码应有的复杂度,没有正确完成类的职责分工,将接受队列,处理队列信息,以及电梯运行管理等方法都塞入了一个类中,导致了单个类运行程序的臃肿和低可读性,同时难以进行功能的扩展。
缺少合理的注释:从图表所示,代码整体存在的注释极少,缺少了合理的代码注释,严重影响了代码的可读性,以及进行代码优化时的困难程度。
心得
在程序设计初期,我将方法和对象集中在一个类中,在使用时清楚地体会到了这种“上帝类”使用的高度不便和潜在问题,通过对比单一类和多个类的实现方式,我认识到合理划分类的重要性,通过多个类分别负责不同的功能与对象,使不同的类能够合理分工合作,各自负责对应的功能,能够显著提高代码以及方法的复用性和可读性、维护性,让我对代码的编写更加方便便利,也让我清楚理解到了坚持单一职责原则的理由和优势,让我在往后的编程中能够记住该原则,大大提高了我在设计类时的正确性以及对复杂代码处理能力。
第二次电梯题目设计与分析(题目集6)
题目要求
该题目在第一次电梯设计题目的基础上,额外增加了要过滤掉相同请求的要求,并且要求必须根据题目给出的参考类图,解决电梯类职责过多的问题,将电梯的处理方法分解到各个类中,以上一次代码中电梯类为基础,保证本次设计的类能够遵循单一职责原则。
程序设计
SourceMontor报表
SourceMontor复杂度表
PowerDesigner类图:
程序设计分析
相较于上一次的题目,第二次迭代题目需要更多的类来分担分工原本单类的职责,因此我额外编写构造了以下几个类
Elevator类:管理电梯的实时状态和基础属性,管理电梯的各种数值
ExternalRequest类:表示楼层外部的电梯呼叫请求,用来接受外部请求输入
RequestQueue类:管理内部(电梯内按钮)和外部(楼层按钮)请求,管理程序需要处理的内部和外部请求队列
Controller类:电梯系统的中央协调器,处理整个电梯程序电梯的运行的总处理,设置和决定电梯的运动状态与方向
问题分析:
在本次电梯控制系统的迭代开发中,我遇到了一个因枚举类型作用域管理不当引发的编译错误。具体表现为:Controller类中声明direction属性时,直接使用了Direction枚举类型(代码片段:private Direction direction;),而该枚举实际定义在Elevator类内部(根据类图应为Elevator.Direction)。由于未通过完整类路径引用枚举类型,编译器无法解析符号Direction,最终导致"cannot find symbol"的编译错误。让我认识到了跨类使用方法与对象时所要遵守的格式。
心得
在本次迭代实验中,我修改了原本的Elevator类,将原本的单类拆解分成多个负责不同功能的类,在这个过程中,我感觉到了我在编程过程中代码可读性的明显上升,在修改和维护代码时效率明显提高,此次实践让我深刻体会到,模块化不仅是代码拆分,更是对系统责任链的精准刻画,为后续引入、组合等进阶优化奠定了可扩展的基础架构,保证了代码优化的效率,让我体会到了面向对象设计原则的重要性。
第三次电梯题目设计与分析(题目集7)
题目要求
第三次迭代题目基于第二次迭代题目,变化了输入楼层请求的格式,同时在处理了外部请求之后,需要将外部请求的靠后楼层数据转移给内部请求队列末尾并继续处理。
程序设计
SourceMontor报表
SourceMontor复杂度表
程序设计分析
在设计该迭代题目时,因为输入信息的修改,MAIN类中的接受信息的正则表达式需要重现修改修正,在队列处理的方法中也要重新修改,以确保在处理外部队列信息后能够将处理后的后置楼层置入内部队列之中。
问题分析:
在进行第三次迭代题目时,总体上因为代码处理逻辑和类的功能分配并没有与第二次迭代题目存在太多的差异,所以Source Monitor工具给出的分析结果中,第三次分析的复杂度与数据与第二次并不存在太多差别,我出现的问题也主要集中在对队列处理的额外要求上,我在一开始忽视了对外部队列移除后给予内部队列的要求和同时面对内部外部队列相同楼层的特殊情况处理,在经过数次修改后,最终优化代码成功解决了要求。
心得
在完成第三次迭代作业的过程中,因为其与第二次迭代作业的差异较小,以至于我没有把太多认真的态度放在题目上,导致了我在完成作业时出现的一系列问题和对题目要求的忽视,而对于题目的迭代代码也仍然存在很多可以优化的问题,而Source Monitor工具所展示的分析结果也说明了代码仍然存在很多可以改进的地方,在往后的作业中,我要以更严格的态度来面对需求。
三.踩坑心得
出现的问题
- 1、对LOOK算法[1]的不理解导致的队列请求信息处理结果错误: 在最开始完成PTA上的电梯编程题目时,我因为整体题目的复杂冗长,忽视了题目对于电梯处理逻辑使用LOOK算法的要求,导致当代码完成时,程序输出的逻辑与题目所给予的样例存在极大的不同与错误,而在意识到需要使用LOOK算法后,初期未理解LOOK算法“同向优先扫描”的核心逻辑,在5楼接收到上行(8↑)与下行(3↓)混合请求时,错误地将反方向请求提前处理。代码误判电梯应当立即调头下降,导致正确路径应为5→8→3却变成5→3→8,以至于电梯数据处理再次出现了错误,在通过反复测试和对样例的观察后,我最终理解了LOOK算法的逻辑,并将其应用在了电梯的处理逻辑当中,才最终使得运行结果正确,可见在编写程序时,必须清楚自身所使用的算法,以避免算法的错误使用导致处理逻辑错误。
- 2、第三道电梯迭代题时忘记外部队列空置后处理内部请求队列时将内部队列的数据给予外部请求列表: 在编写移除队列末位请求的方法时,我忽视了内部队列空置后,外部队列在处理完并移除一个请求后,需要将该请求所包含的楼层给予内部队列的要求,只进行了两个队列均存在请求时所需要的请求转移逻辑,导致在内部队列全部移除后,单独处理外部队列时,代码未能按要求把需要转移请求的置入内部请求的队列结尾,导致了最终结果的缺失,在我重新补上该情况下的处理代码后,问题立刻解决了,可见在对代码进行维护和修改时,必须要考虑到其涉及的各种方法,以及可能影响到的内容。全面的处理改进代码。而在最后很轻松的修改就维护了代码
- 3、没有给代码添加合适的注释导致的代码可读性极低: 在不断改进电梯迭代代码的过程中,因为没有在一开始为方法和对象添加合适的注释,我经常性会遇到看到自己写的代码,却一时无法立刻想起其功能的问题,而在我想要找到某处具体代码并对其进行改进时,我同样因为没有任何的引导,导致了我难以找到我需要找到的代码所在的位置,因此我深深地感受到了在没有注释的情况下,代码的可读性被大大降低,严重影响了我在修改和编写代码时的便利性和速度,因此在未来的程序编写中,一定要为代码添加合理且清晰的注释,保证代码应有的可读性。
- 4、在一开始使用正则表达式时,没有熟悉正则表达式的使用格式,导致无法正确接受输入信息: 初次使用正则表达式解析电梯请求时,因对语法规则不熟,常陷入符号输入错误。例如试图用\d+提取楼层数时,却漏处理方向标识,导致Main类误将输入信息识别为非法输入。更糟的是,错误的匹配使请求队列混入错误,引发Elevator.processRequests()方法无法处理请求信息。后来通过分步解析策略改进,严格分割数字与方向,再独立校验各字段合法性,再正确捕获所需要的请求信息。为此在正则表达式的使用上我得到了深刻的教训,正则表达式的判定标准较为严格,必须通过精准的模式设计+单元测试才能避免输入的信息不会出错。
四.改进建议
针对三次电梯程序设计作业中暴露的共性问题,我提出了以下系统性改进方向:
架构设计优化:进一步遵循单一职责原则,将电梯功能模块拆分为请求管理、调度算法、状态控制等独立组件,通过接口定义模块交互规则,降低代码耦合度,提升功能扩展性和代码的复用性,进一步保证代码的单一职责。
代码可读性增强:完善核心算法与关键逻辑的代码注释,避免代码难以看懂,难以找到所需代码的情况,提高代码的维护效率,保证代码的可读性。
五.总结
在PTA题目集5~7的电梯程序设计系列作业中,我通过三次迭代逐步实现了从单一电梯调度到复杂多队列处理的场景模拟,围绕面向对象编程原则展开实践,初期因将功能集中至单一“上帝类”导致代码臃肿,后续通过拆分请求管理、状态控制和输入解析模块落实单一职责原则,利用枚举类型封装电梯运行状态提升代码健壮性;过程中深入应用LOOK算法优化调度逻辑,初期因误解LOOK算法,未能理解电梯运行规则导致路径错误,经反复测试后明确算法内核,同时借助正则表达式分步解析混合请求,最终在代码复用性、注释规范性和算法理解层面获得显著提升,尤其意识到需求分析前置和模块化设计对复杂系统的重要性,为后续开发积累系统性调试经验与结构化设计思维。经过这几次的PTA题目训练,我对于构造类以及单一职责原则的设计原则已经有了更加熟练的使用,可以正确地设计为代码需求构造所需要的各种负责不同职责的类与相应的方法对象。
而通过在代码设计过程中所多次遭遇的坎坷和踩坑,我也愈发发现到了自己在程序设计过程中的不足,在进行代码编写时不够细心也不够严谨和有条理性,才导致了在完成题目过程中屡次出了技术性问题。为此在往后的编码过程中,我会用更加严谨的态度来处理算法等需要严格原则来设计代码,在进行迭代作业的过程中也不能只追求通过测试点,而应该抱着学习提高自己的能力的目的不断为自己查缺补漏。
建议与意见
在前几次的迭代作业中,题目对于电梯运行规则的描述不够清晰,即便需要考虑到对学生能力的考察,但对于运行的规则描述仍然应该保证不会引发过度的误解,以及题目测试点的分数分配也着实不合理,将过多分数集中在一个测试点上对于难度较高的题目来说很容易消磨做题者的耐心和信心,以及继续做题的意愿,我认为应该将分数分配给更多的测试点,循序渐进地让学生在代码优化中逐渐提高做题和继续完成题目的欲望。也能帮助学生逐步提升编程能力和思想。
附录
(改进后的主类)
public class Main{
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String data;
int F=-1;
int f=-1;
int Nfloor=0;
int floor=0;
int nfloor=-1;
int minFloor,maxFloor;
String request = "";
ArrayList<String> list = new ArrayList<>();
data = input.next();
while(!data.equalsIgnoreCase("end")){
list.add(data);
data=input.next();
}
minFloor = Integer.parseInt(list.get(0));
maxFloor = Integer.parseInt(list.get(1));
Elevator elevator = new Elevator(minFloor, maxFloor);
RequestQueue Queue=new RequestQueue();
for(int i=2;i<list.size();i++){
request = list.get(i);
if(request.contains(",")){
if(!request.matches("<\\d+,\\d+>")){System.out.println("Wrong Format");}
String[] parts = request.replaceAll("[<>]", "").split(",");
floor = Integer.parseInt(parts[0].trim());
Nfloor= Integer.parseInt(parts[1].trim());
if(floor>=minFloor&&floor<=maxFloor){nfloor=Nfloor;
F=floor;
Queue.addExternalRequest(floor,Nfloor);}
}
else
{
if (!request.matches("<\\d+>")) {
System.out.println("Wrong Format");
}
floor = Integer.parseInt(request.replaceAll("[<>]", ""));
if(f!=floor&&floor>=minFloor&&floor<=maxFloor){f=floor;
Queue.addInternalRequest(floor);}
}
}
Controller control=new Controller(elevator,Queue);
while(!control.getQueue().getInternalRequests().isEmpty()||!control.getQueue().getExternalRequests().isEmpty()){
control.processRequests();
}
control.over();
input.close();
}
}
LOOK算法是电梯调度算法(Elevator Algorithm)的优化版本,常用于磁盘I/O调度或电梯运行控制。其核心逻辑是:在移动方向上优先处理同向请求,若无请求则立即调头,而非移动到物理端点(如SCAN算法)。 ↩︎