三次电梯调度程序
前言:
三次的作业均是围绕“单部电梯电镀程序”展开的。三次作业的题意如下:设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。
请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。
我第一次看到这个题目的时候是有点傻眼的,一方面因为题目很长,另一方面要求很多,也有很多自己没有接触到的知识点,优先处理,队列的实现,类的单一职责,通过正则表达式来区分接收外部请求和内部请求。第二次作业在第一次作业的基础上增加了类设计要求,强调单一职责原则,每一个类都只完成与自己相关的职责。这又添加了一些难度,因为对于我来说,能否分清楚某件事是否由这个类来完成是有点困难的,容易搞混。第三次作业进行了迭代行设计,并且又加了一个很新颖的设计:对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾),其实我看到这个变动规则先是疑惑,然后是不确定,疑惑的是这个变动是什么意思。不确定的是,难道真是这个意思?(真的假的?)题量看似就只有三题,但却是真真实实的印证着老师那句话,给了一周的时间便是一周的工作量。难度:就单单这个队列设计对于我来说就是很棘手的一件事。所以,难度真真实实是有的。
设计与分析:
第一次结果是:运行超时了。从代码开始到代码代码结束,进行了根据正则表达式判断该请求是属于外部请求还是内部请求。然后对电梯类的最大小楼层的接收,另外设计方法判断该楼层的请求是否属于同方向的请求,以及设计相应的方法判断该楼层的开门与否。进行简单的输入,能够得到结果:
但是对于复杂的输入,也就是请求多的输入,就会输出错误。
归根结底是我的想法太混乱了,不清晰,不知道怎么去实现队列的正确处理。
我把第一次的代码放在SourceMontor中进行分析:得到了如下图:

优点是整体的复杂度较低,有利于理解以及后续的维护,对于出现的一些问题也可以更快速进行排查。缺点便是可以看到这个代码的最大复杂度为7,有些高,说明Lift.start()方法的方法逻辑相对复杂,在一定程度上可以进行拆解以及优化。最复杂方法行数为89,在复杂度偏高的同时行数也偏多,理解起来会相应的比较困难,这也是缺点之一。此外,注释行占比偏低,说明在代码中的注释比较少,在代码后期进行维护和修改的时候可能会形成较大的困扰。
第二次作业是在第一次作业的基础上,进行类设计,我在第一次代码的基础进行分开调整,但是在类设计这一个关卡中就遇上了很大的麻烦。还是出现在类设计的不够好,模棱两可,界限不清楚。其次,对于有重复输入只需要合理保留一个也十分吃力。所以很遗憾第二次的作业也没有完成。
到了第三次作业,有了前两次作业的铺垫,更没有那么吃力了,也对代码进行了相应的调整和改进。终于通过了3次测试样例,虽然没有完全通过,但还是很满足了。
同样我把这次代码放在了SourceMontor中进行分析,得到了如下图:

从中得到优点就是:代码有了一定程度的功能封装和模块化,结构相对丰富。平均复杂度不高,有利于后续的优化。虽然通过了3个测试样例,但是相应的不足也体现出来了。首先,代码行数偏多,就体现出来了其中的逻辑可能比较复杂,或许把简单的问题复杂化了。其次,最高复杂度为17,十分高,说明方法的逻辑十分复杂,测试难度也十分大。也间接地反应出我对这个题目理解不够透彻,还没有找到比较合适的方法去解决这个问题。另外,就是方法调用语句偏多,不仅仅反映出这个代码可能调用过程存在不合理性,还反应出我对各个方法之间地职责没有理解到位,就很容易绕弯子,给自己也添加了不少地麻烦。最后就是在代码注释这方面,代码注释量太少了,但是代码行数又偏多,在后面的修改过程中对自己理解起来也很不方便。这方面需要改进。
踩坑心得:在第一次编写代码的时候,没有理清题意就着急动手。花了很长时间去完成一个错误地题目,错误地理解成电梯的运行过程是按照平时生活的按电梯的模式进行运行。后来老师在课堂上对这个题目再次进行解释地时候自己也是大为震惊,辛辛苦苦写了那么多竟然是错误的,只好从头再来。这也让我自己明白了心急吃不了热豆腐。关于这个要读懂题意的踩坑心得,我以这部分代码作为参考:

这部分代码是获得用于获得下一个要执行的楼层。对于第三次题目,给了要求,在执行外部楼层后,要将目标楼层给内部请求的队尾,但是由于没有理解到,就只是单方向地进行执行,也没有处理相对应的目标楼层,为此也浪费了不少时间
知己知彼,才能百战不殆,写题目也是同样的道理。在动手之前一定要理清楚题目的用意。此外,还让我知道了java真的是面向对象的语言,在某些程度上真的给我们减少了很多的麻烦。例如,以这部分代码为例:

就单单“equals”和“remove”这两个就让我头疼了好久,我想,要有队列,还要有头队列,此外,还要有方向的考虑,我到底怎么知道他是不是同方向啊,那这个执行完后的又怎么让它“合理”的消失,从而执行下一个指令呢?经过不断地踩坑可尝试,终于发现这两个法宝。真是太方便了!!
改进建议:虽然我的最终答案没有获得全对,但是通过这么长时间所写过的代码,我觉得代码可以进行如下方面的改进,第一:给出相应的注释,不仅仅有利于自己读懂,像这次的题目总共分为了三次作业,每次虽然都有相应的改动,但是大部分的逻辑是没有变动的,如果没有注释,在后面进行改进的时候就很容易忘记自己当时的想法,也就会效率很低。其次,关于类之间的关系应该先充分打好基础,明白应该设计哪些类,类的职责是什么,然后在编写类之前,想清楚类与类直接的关系。最后就是对于每一个方法,其复杂度不应该太高,可以多花时间想清楚具体的实现方法,再去写这个方法。不然复杂度太高,既不利于自己读懂,在后续的改进中,也很容易成为一个绊脚石。
(插入三次的作业的做题感受,写第一次作业的时候,抛开读不懂题目不谈,是真的有点崩溃,因为存在知识的溃泛,对这个题就只是感觉到无力感,因为不能充分利用好java本来就有的方法,就会觉得这个题怎么会这么复杂,怎么才能写到头。写的时候也是像无头苍蝇一样,想到一个点就写一个点,但是效果就像打补丁一样,东补补西凑修,但是怎么弄都感觉差点意思。然后经过这三次的写题经历,也开始明白有一个通透的思路会事半功倍,其次也是很重要的一点,要好好利用已有的方法!)
总结:通过这三次作业,最让我印象深刻的就是学会了两个队列的创建与实现,以及优先处理这个算法。然后对ArrayList和LinkedList这两个也是见识到了它们的强大之处。太便捷了。例如那些什么删除元素,添加元素的,在我们之前的c语言中是相对比较复杂的,但是在java中,就是简简单单一个语句就能实现,所以平时无聊的时候就可以多看看java给我们已经提供了哪些现成的方法,说不定哪天就派上用场了。此外,对于类之间的关系,方法的实现这一块,需要多花时间去理解底层逻辑,才能写出更好的代码,同时也能给自己减轻不少工作量。关于一些建议的话,我觉的老师可以在课前告诉我们当堂课要学会的大纲,这样我们心里有底,也更容易把握重点。然后作业这一块,在完成之后,可以分享一些优秀的代码,供我们学习。然后其实我一开始好奇过为什么老师不给我们演示一道题的全过程,如何完成一道题,但是后来说了一句话“不是教你们写代码,是设计”,我也才明白过来。(虽然还是很希望能够讲一讲)
浙公网安备 33010602011771号