OO Unit 2 电梯调度

OO Unit2 博客作业

基于度量来分析⾃己的程序结构

在这里插入图片描述
自认为自己的架构不算特别复杂,一个电梯类,一个请求队列。代码量也挺少。

复杂度分析

类复杂度:
在这里插入图片描述

方法复杂度:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
看了idea自带的代码分析,和我原先想的一样。复杂度最高的是电梯类和请求分析类。请求分析类因为要每次更新请求列表,SCAN下一个目的楼层,所以复杂度自然就高。其实我的代码也没有其他东西了。

架构分析

大概的思路如下:
调度器
在这里插入图片描述
在这里新请求分为三种

  • 能直达
  • 不能直达,从起点到换乘楼层
  • 从换乘楼层直达目的楼层

如下所示
在这里插入图片描述

电梯
在这里插入图片描述

非常简单的想法,没有任何优化。最多是作业二的时候,用的不是ALS,而是类似于SCAN的一种算法,但其实性能分也不是很高,作业三只确保了正确性。

改进和重构

本次电梯单元三次作业都没重构。三次作业祖传电梯,没写优化,有很大的改进空间。

架构方面

  • 电梯类可以分成三个子类,继承电梯父类。这样比较OO,但其实我觉得不必要。

  • 请求队列只有一个我觉得可以避免很多同步问题,所以暂时不打算改。

  • 电梯的run方法很长,肯定有设计上的不合理,有代码重复的情况。修改:应该把这部分重复代码另外写一个方法,两次调用。又因为重复之中还有个别语句不同,可以在调用方法的时候传参,然后选择性执行个别语句。我不知道这样写是不是符合Java设计,但现在我只能想到这样处理,但觉得不是很规范。
    将电梯不同的动作分别写成不同的方法,而不是在一个run方法中将所有动作都详细实现。

  • 调度器是否需要写成一个线程?
    在研讨课上,同学们分享了自己的架构,大多数人还是写了调度器线程的,不管是否鸡肋。可能这样比较符合实际,但是我觉得,如果调度器线程真的在负责电梯任务的调度,就应该给他一个线程,否则就没有必要单设线程了。
    我的调度器线程应该可以省略,因为调度工作由电梯自己完成,其实我也没有进行任务调度,请求给三个电梯,然后三个电梯抢着去接人,这也是我只有一个请求队列的后果。

性能优化

  • 电梯提前去换乘层
    需要换乘的人,应该在刚输入的时候,就把他即将要换乘的楼层列入备选目的楼层之一。针对的数据样例:FROM-1-TO-16,可以预先让A上15层,而不是B或者C从1楼把人接到15楼之后,A再行动。
    研讨课上一位同学分享了这个问题的解决思路,他的做法是设置一个buffer,存储这种换乘的后一段接送任务。当电梯没有当务之急的请求,就去执行在buffer中的请求。
  • 空闲电梯去中间等候
    但因为测试数据随机分布。但其实也并不知道数据是怎样分布,可能事倍功半,故本条鸡肋。
  • 预选调度电梯
    每个人进来之后,计算各个可停靠的电梯如果加了这个人,各自会花多少时间去接送。以及以何种顺序接送这个人更省时间。
    但是,基于当前电梯的状态去选择最佳电梯,也只是当前情况下的最佳。而电梯的运行是一个动态的过程而且也在不断加入新的请求,所以可能当前情况下的最佳并不是全局的最佳。
    在研讨课上我问了一位同学,他表示确实存在这样的局面,但是由于我们不能预知未来,所以只能选择当前情况下的最佳。如果不能离线运行,而非要在请求刚进入的时候就调度完毕的话,大概也只能这样了。
    所以我觉得自己三个电梯去抢人这个“不调度的算法”,也不算太差。
  • 贪心算法
    即上了2层回头接1层的人。这个需要比较回头接和送完人之后再回来送这个人之间性能差异,所以需要知道这个人的方向和电梯方向是否一致,以及这个人的起止楼层和电梯的起止楼层比较,权衡的最好方式我认为还是各自计算一下时间。

发现过的BUG

  • 最后一条请求门没关
    应该是在关门之前就结束整个程序了,我知道我用System.exit(0)强制退出可能不太好,应该让每个程序都结束run自动结束进程,然后从main函数退出程序。从讨论区和群里又看到可以用join()函数将main和线程联系起来之类,可能和个人架构有关。在5行代码以内解决这个问题的方法就是开门sleep400,关门sleep0,有点狡猾。
  • 超载
    这个在课下就改了。在人进入电梯之前检测容量。
    但是,如果要加入贪心算法等这些要特地回头接人的算法的时候,如果电梯此时已经满员,就不应该再回头接人,就相当于不应该再继续响应任何请求,包括顺带的和不顺带的。
  • -4层和21层
    超过楼层限制这个问题我没出,大概是没有及时停止电梯运行。
  • 4层到3层,2层到3层
    因为电梯的停靠楼层限制,上下一层还得换乘。在换乘调度的时候分类清晰,路线规划,不能陷入上上下下还到不了终点,或者直接不接人。所以重点在调度要路线清晰,可以不唯一(根据电梯此时方向进行动态调整),但是一定要清晰。
  • CPU超时。
    是轮询的问题,debug方法就是在每个while语句块里加debug打印输出信息,并且用命令行执行,看是哪个while循环偷偷轮询了。多线程我觉得经常会在我自认为不会出现while空跑的地方有很短暂的空跑。

简化问题

我觉得写程序最重要的是简单,思考问题也是。电梯类的设计,请求队列的设计,甚至是退出程序的设计,都可以思考一下可以怎样简化。这样会少很多写代码和debug的时间,我相信就目前的OO作业来说,架构越简单,bug就越少,因为根本无处滋生bug。我的意思并不是修复漏洞的补丁和bug之间的关系,只是说整个架构应该尽可能简化。
一个好的模型很重要。建模的时候不应该陷入“如何实现”的工程细节,而应该站在代码之外,高度抽象概括问题。
那么电梯作业的逻辑就是:电梯获得新请求-》去接-》直达则送达-》不直达则送至换乘层-》换电梯接-》送达。这样一个路线。
剩下的都是工程细节的问题,不在设计环节考虑。

结束程序

曾有同学问我结束程序的模块应该怎么写,我的想法也很简单,就是停止输入+请求队列为空=结束。因为不能轮询,所以设计为:当电梯即将进入等待状态前唤醒检测一次(是否满足停止输入+请求队列为空),若是则结束程序,若不是则检测机制再次进入等待。所以最终结束程序的条件概括为:三个电梯都处于等待状态--》停止输入+请求队列为空。

一个电梯类

本程序中只有一个电梯类,虽然可以写成三个类继承一个电梯父类,但我觉得我们这个题没有太大的必要,因为只有个别数据不同,例如上下速度,停靠楼层,载客量,这些数据都可以用一个私有方法get,没有必要写新的类。

一个请求队列

以及只有一个请求队列,在我构想的模型中,所有的人都在电梯间等待,电梯间里有1/2/3个电梯。对于人来说,人们看到电梯开了门就上,拥挤在一团并没有提前排成三列,也没有精神洁癖非得上哪个电梯。对于电梯来说,电梯只管自己跑楼层,不管载客量多少,人们是直达还是换乘,外面还有多少人,电梯自身不思考这些。
乘客自己需要对自己要去哪负责,而现实中确实如此。乘客上了电梯之后自行决定在哪一层下,或目的楼层,或换乘楼层,然后告知电梯,电梯实际上不知道这个楼层是目的楼层还是换乘楼层,也不需要知道这个。人们若是到达了目的地就从电梯间走出,若是没到目的地就继续待在电梯间里,显然他现在的起始楼层变了。

多线程初探

这单元是第一次写多线程的程序,碰到过一些很迷的状况,但大多是我自己程序别的地方出了bug。多线程就像按下葫芦浮起瓢,有很多东西要同时考虑,但其实在具体实现的时候,在写代码的时候,还是葫芦写完写瓢,瓢de完de葫芦,不可能也没必要一边写葫芦一边写瓢。其实还是一个一个把模块写好的过程。
看了一些大佬对于电梯性能的魔鬼优化,确实很周全,做了我能想到的一切,并且真实实现了,确实很厉害。可能下次作业我也可以稍微加入一些性能优化,有时间的话。

原文地址:https://www.cnblogs.com/ffchyan/p/10736853.html