面嚮對象程序設計第二單元博客作業——熟悉多綫程

本单元OO作业主要设计电梯的调度。作业情况为:第五次多线程单电梯基础调度,第六次多线程单电梯智能调度,第七次多线程多电梯智能调度。

第五次作业

(1)多线程协同和同步控制

  设计了电梯线程和请求接受线程,采用单生产者-单消费者模式,设计了一个缓存池(ArrayList)用于存储。

  协同:请求接受线程首先接受请求,将请求放入缓冲队列;当队列不空时,电梯线程从缓冲队列取出第一个请求执行,循环至空。同步控制采用synchronized语块,仅有缓冲队列需要被同步。

(2)度量结构分析

  代码量比较小,结构分析简单。

  单一责任:各方法功能较少,未出现功能重叠现象。

  开放封闭:与后续作业有较多不同,整个对后续作业参考意义不大,致使后续作业重构部分较多,因而该部分一般。

  里氏替换:未采用继承。

  接口使用:未采用接口。

  依赖倒置:未出现依赖倒置。

(3)bug分析

  本次作业测试难度较小,未发现bug

第六次作业

(1)多线程协同和同步控制

  增加了队列管理线程。依然采用单生产者-单消费者模式。调度器与请求接受线程之间有初始队列共享;调度器和电梯间有上行和下行队列。

  协同:请求接受线程将请求交给调度器,调度器根据请求分为上行请求和下行请求分别置入上下行队列,电梯分别执行两队列的任务:在停止状态将某一队列的全部请求加入执行,在上行或下行过程中,将同向队列符合捎带条件的请求加入执行队列:。电梯状态分为:停止,上行,下行。停止时,电梯无任务可做,或者刚完成电梯内部的执行任务。上行:顾名思义。下行:顾名思义。符合捎带条件:当电梯刚刚抵达某一层时,该请求已经存在于上行或下行队列中,且与电梯运动方向相同,且其出发层数符合要求(简单而言:如果电梯在上行,则该请求的出发楼层大于等于电梯当前楼层;如果电梯下行,则该请求出发楼层小于等于电梯当前楼层)。电梯会因为新的请求的加入修改其最终目的楼层(如电梯中有人上行至10层,后来捎带的人需求上行至13层,则电梯的目的楼层从10层修改为13层)。同步初始请求缓冲队列,上下行队列,采用synchronized语块。

(2)度量结构分析

  第二次作业中判断切换比较多,赋值操作比较多。逻辑略显复杂,因而导致了整体的方法的复杂度较高,使得检测较为困难。service方法服务于上下楼层的请求,通过调度up/downstairs方法实现楼层的转移。举上行为例:若电梯初始状态高于请求出发楼层,必须首先下行至出发楼层,抵达出发楼层前的状态为下行,使用downstairs方法,抵达后转为upstairs方法;若电梯初始状态低于出发楼层,则调用upstairs方法。

  单一责任:上下行楼层的方法当中又增添了从上下行队列当中获取可捎带请求的任务,分离不够明确。

  开放封闭:因为以后的代码需要进行修改,关于人数限制的部分。由于人数限制问题也对捎带策略进行了调整,因而导致了较大规模的变动。

  里氏替换:未采用继承。

  接口使用:未采用接口。

  依赖倒置:未采用抽象类,高层无需依赖底层模块。

(3)bug分析

  CPU运行时间问题,一开始采取轮询策略,后来替换为检测状态休眠的策略。有捎带策略的bug,在本次未被检测出,将在第七次进行分析。

第七次作业

(1)多线程协同和同步控制

  电梯数增加为三。依然采用单生产者-单消费者模式(尽管缓冲队列增加,但是由于每个缓冲队列只有一个生产者和一个消费者)。调度器与请求接受线程之前有初始请求共享队列;调度器与每台电梯之间有两个队列:调度器作为生产者而电梯作为消费者的任务队列,调度器作为消费者而电梯作为生产者的换乘队列。构建了新的类用于管理请求,该类的每个对象中包括一个原始的PersonRequest对象,和两个int型变量,分别指代该请求的出发楼层和到着楼层,电梯即按照新的对象的出发层和到着层执行任务。调度器在接收了初始请求之后计算它是否需要换乘,是则计算给出换乘楼层作为该请求的到达层(出发层保持原请求不变,到达层替换为换乘层),并将它安排给出发楼层可达电梯的任务队列,否则直接将其安排可执行任务电梯。某一电梯执行完换乘请求的第一部分之后,重新计算该任务的出发层和抵达层(将出发层替换为换乘层,抵达层替换为最终目的楼层),并将之加入换乘队列,供调度器取用。在我的设计当中,电梯不管自己能停的楼层,一切归调度器决定,调度器指派给某电梯的任务一定是它可以抵达的楼层(即出发和到达都在该电梯的运行范围内)。同步七条队列,请求接受线程和调度器间的初始请求队列,调度器和三电梯间的任务队列和换乘队列。

(2)度量结构分析

  Elevator类的私有变量非常多,用于存储各种状态和队列管理。其方法数量也大。Elevator类与其他类平衡性不高,不过目前没有想出来很好的解决策略,关于平衡各类之间的量的问题。

  相较于第二次在某些方法中减少了一些不必要的逻辑判断,在另一些方法中增加了逻辑判断。比如减少了arrange方法的逻辑判断,却因为人数设置的问题增加了up/downstairs方法的工作量,致使代码复杂化程度提升。

  单一原则:上下行楼层方法增加了获取可捎带任务的判断,仍然破坏了该原则。

  开放封闭:如果有其他需求,可能还是需要修改代码,无法直接增添。

  里氏替换原则:未采用继承。 

  接口使用:未采用接口。

  依赖倒置原则:未采用抽象类。

(3)bug分析

  结束条件的判断。最开始判断错了停止条件,导致任务尚未执行完毕时结束了输出。后来加上所有队列为空且读入null时才结束执行的判断,解决了这一问题。

  在捎带过程中的问题,捎带目的楼层的改变没有做好删除操作,导致在特定条件下的请求在执行完成之后没有被提出而电梯则反复执行该任务,重复输出到达层。之后修改了剔除条件。

  在强测中被hack了两个点,重复输出的问题。检查了一下是自己的电梯执行的过程中的楼层运行没有计算好,导致重复出现的输出。在以后的作业当中需要特别注意类似问题。

心得体会

  三次作业难度分别递进。第五次作业的主要任务目标是帮助我们熟悉多线程,由于之前没有接触过多线程编程,本次作业开始做起来还是有一点吃力,因为很难理解自己在做什么,需要什么。

  第六次作业是在第五次基础上加难,需要电梯进行捎带调度。不过相较于最开始的“万事开头难”,第六次作业只是在调度策略上有所改变,略显麻烦。本次调度主要检查能否处理同向请求。我个人的操作比较简单,每当电梯停到一层,就会扫描同向的队列,判断是否有符合条件的可捎带请求。就本次作业而言我个人以为捎带策略不算很差,但是没有预期到下次作业的可能扩展性,导致下次的稍带比较麻烦。

  第七次作业在第六次的基础上限定了电梯的运行范围和电梯的限乘人数。由于会有换乘情况的出现,线程之间的交流比之前的作业更加复杂,生产者和消费者增加。

  整体而言,这三次的作业难度在于对多线程的理解和把握,如何设计合理的线程沟通策略是问题的关键所在。难度较大,不过处于可接受范围之内。

  作业的调试确实是不小的挑战。因为自己的电梯运行状态和输入的时间没有办法精确把控,有时想尝试掐点的任务发现不太可能实现。测试集还是需要广泛。在第七次作业临截止前,我突然想到自己的作业里会有bug,修改之后发现这个bug同样存在于第六次作业当中,而且并非刁钻的bug,问题延续了两次作业。因此以后还是要多做测试。  

  还需要说的一点是设计很重要。第五次作业由于不熟悉多线程,所以对后面的作业没有什么帮助,基本全部重构了。第六次作业考虑了一部分可能的扩展需求,因而在第七次可扩展的时候省却了一些时间。但是还是有设计上的缺陷,比如第六次没有设计好人数限制的问题,导致第七次的人数限制上非常复杂。

  一定强度的测试可以帮助大家磨炼心智,增强抗压能力,提升coding水准。毛主席有言:“圖逺者,必有所待;成大者,必有所忍”(《毛澤東早期文稿》,44页)。OO的意义不仅仅在于熟悉一门Java语言,体会面向对象的思维,也在于如何面对自己写过的险恶代码,在万般不情愿当中diss自己犯下的错误。这种痛苦是磨炼自身意志品质所必须经历的事,OO大规模的训练很好地帮我们很多人认清了自己的不足,帮助我们成为更加优秀的存在(虽然确实有点痛苦,但是我们可以换一种积极的方式:如果现在不吃点苦,放着以后吃吗;现在吃不得这种苦,以后还能吃更多苦吗?)。想想如果以后到工作中,客户不会帮你设计一套标准来debug,有问题按照合同扣钱就是了。所以现在的训练也是一种对于未来而言很好的铺垫吧。

猜你喜欢

转载自www.cnblogs.com/runnan-shen/p/10761918.html