OO第二次博客作业

OO第二次博客作业

 

前言

OO课程已经学习过半,回顾一路写过来的作业:

第一阶段三次作业主要让我掌握了面向对象的基本规则,学习并能够熟练使用面向对象的三个基本特征——封装,继承,多态。

第二阶段的作业主要学习了java多线程编程,也有了不同以往的编程体验。解决不容易复现又难以琢磨的线程BUG是多线程学习中最为激动人心的过程。

本次博客作业主要分析第二阶段三次作业中设计策略,程序结构,bug分析,心得体会。

设计策略

第五次作业

     第五次作业是阉割版单电梯的调度问题,整体十分温和,但是对于第一次接触多线程的我当然是不会写。这次作业也为我们无意间打开了单线程实现的大门,这是一个有效避免bug,又能避开陌生的多线程的好办法,但是用单线程提交作业的多是多线程已经几乎炉火纯青又要分数求稳的julao,所以,我还是坚定使用多线程的实现方法来完成这次作业,毕竟“多线程有债必还“(手动滑稽)。

     结合课上老师的讲解,我发现这是一个典型的生产者-消费者模型,我又在《图解多线程》一书上查询了该模型的更为详细的的例子,就把电梯这一作业划分成为三部分:生产者——乘客请求,消费者——电梯运行,容器——乘客队列的数据结构。这样一来就分门别类地实现就好了。

在具体实现电梯的过程中,主要难点是对于加锁的把握,多加锁容易造成死锁,少加锁可能导致线程不安全。最后在不断的测试中总算解决了这一问题。

本次作业的程序结构如下图所示:Thread1.java代表输入线程,Thread2.java代表输出线程,两线程通过People.java进行通信。

 

第六次作业

     第六次作业相较于第五次作业,变得更像是一部更贴近现实的电梯,需要实现可以捎带的电梯,也就是可以在上楼或下楼的情况下就把上楼或下楼的乘客顺路一波带走。实现这一电梯,第四次作业的架构仍然适用,所不同的是,这次的电梯设计每个楼层都设计了相应的队列,分为上楼与下楼队列,很好的模拟了电梯上下楼捎带乘客的场景。但是其本质上仍然是生产者-消费者模型的使用。

     本次作业的程序结构如下图所示:相较于上一次作业的程序结构图,可以发现主要变化在于两点:1.ElevatorRun.class类中检查楼层停靠时是否需要有乘客进入电梯;2.People.java作为基础数据结构,构造每个楼层相应的上下楼乘客队列,如此使得电梯运行上行专乘上行乘客,下行专乘下行乘客。

 

第七次作业

     第七次作业是真正意义的多线程作业,三部电梯同时调度,还有各个楼层停靠的要求,也是我翻车的一次多线程作业。

     在作业过程中,首先,我按照前两次作业一样,开一部电梯,并对这部电梯进行可扩展的修改,然后实现在不考虑不停靠的前提下,同时开三部电梯能够很好的运行。其次,我就打开三个线程,开始对每部电梯进行个性化配置,实现不同楼层停靠,载人限制,运行时间设置。然而,这一设计过程带来了巨大的隐患,即由单电梯到多电梯的过程中,每部电梯的调度都是独立实现的,这就缺乏一个顶层的调度模块,每个电梯自己调度自己的,人进入电梯基本随缘,运行时间,换乘次数不可控,导致中测人数稍多的数据就翻车。当然,最重要的原因是我花在本次作业上的时间并没有随难度的增加而增加,所以当我发现没有一个统一调度模块没法有效调度时,已经来不及进行重构。这是我对于本次作业的反思,无效作业,一次就够了,只能在后面的学习中痛定思痛,多花时间。

     本次作业程序结构如下图所示:可以看到,由于过度依赖上两次作业的架构,没有顶层的调度来支持,每部电梯的调度设计越来越臃肿,几乎到了难以为继的地步。

程序分析

参数解释

     程序分析使用IDEA JAVA的插件Calculate Metrics对每次作业的类和方法进行基于度量的复杂度分析,当然,首先最重要的是搞懂主要的三个参数的意义。

1)ev(G) :Calculates the essential complexity of each non-abstract method. Essential complexity is a graph-theoretic measure of just how ill-structured a method's control flow is. Essential complexity ranges from 1 to v(G), the cyclomatic complexity of the method. (即表示一个方法的结构化程度,值越大则程序结构越“病态”)

2) iv(G):Calculates the design complexity of a method. The design complexity is related to how interlinked a methods control flow is with calls to other methods. Design complexity ranges from 1 to v(G), the cyclomatic complexity of the method. Design complexity also represents the minimal number of tests necessary to exercise the integration of the method with the methods it calls. (即表示一个方法与其调用的其他方法的紧密程度,值越大则越紧密)

3)v(G):Calculates the cyclomatic complexity of each non-abstract method. Cyclomatic complexity is a measure of the number of distinct execution paths through each method. This can also be considered as the minimal number of tests necessary to completely exercise a method's control flow. In practice, this is 1 + the number of if's, while's, for's, do's, switch cases, catches, conditional expressions, &&'s and ||'s in the method. (即表示一个方法的穷尽每一条路径所需要的次数,也就是循环复杂度)

4)OCavg :Calculates the average cyclomatic complexity of the non-abstract methods in each class. Inherited methods are not counted for purposes of this metric.

5)WMC :Calculates the total cyclomatic complexity of the methods in each class.

作业分析

     第五次作业和第六次作业总体难度不大,使用复杂度分析工具并没有发现很大问题,程序的复杂度尚可,依赖度也尚可,具体数值如下:

第五次作业

Method ev(G) iv(G) v(G)
ElevatorRun.ElevatorRun(PersonRequest,int) 1 1 1
ElevatorRun.doorClose(int) 1 2 2
ElevatorRun.doorOpen(int) 1 2 2
ElevatorRun.getOutputState() 1 1 1
ElevatorRun.getStop() 1 1 1
ElevatorRun.loadPerson() 1 1 1
ElevatorRun.moveIn(int) 1 1 1
ElevatorRun.moveOut(int) 1 1 1
ElevatorRun.moveTime(int,int) 1 2 2
ElevatorRun.setOutputState(Boolean) 1 1 1
Main.main(String[]) 1 2 2
People.getInputState() 1 1 1
People.getPerson() 1 1 1
People.isEmpty() 1 1 1
People.setInputState(Boolean) 1 1 1
People.setPerson(PersonRequest) 1 1 1
Thread1.Thread1(People) 1 1 1
Thread1.run() 3 4 4
Thread2.Thread2(People) 1 1 1
Thread2.run() 1 4 4
 

 

Class OCavg WMC
ElevatorRun 1 10
Main 1 1
People 1 5
Thread1 2 4
Thread2 2 4
 

第六次作业

Method ev(G) iv(G) v(G)
ElevatorRun.checkFloor(int) 3 2 3
ElevatorRun.doorClose(int) 1 2 2
ElevatorRun.doorOpen(int) 1 2 2
ElevatorRun.elevatorArrive(int) 1 2 2
ElevatorRun.elevatorDown() 1 1 2
ElevatorRun.elevatorGetIn(PersonRequest) 1 2 2
ElevatorRun.elevatorMove(int) 1 5 5
ElevatorRun.elevatorUp() 1 1 2
ElevatorRun.getStop() 1 1 1
ElevatorRun.isEmptyElevator() 1 1 1
ElevatorRun.moveIn(int,int) 1 1 1
ElevatorRun.moveOut(int,int) 1 1 1
Main.main(String[]) 1 2 2
People.getPerson() 1 1 1
People.isEmpty() 1 1 1
People.setPerson(PersonRequest) 1 1 1
QueueAll.getInitPerson(int) 4 3 6
QueueAll.getInputEnd() 1 1 1
QueueAll.getPerson(int,int) 4 4 4
QueueAll.init() 1 1 2
QueueAll.isEmptyFloor(int) 2 4 4
QueueAll.isEmptyFloorEach(int,int) 4 4 4
QueueAll.isEmptyQueue() 1 1 1
QueueAll.setInputEnd(boolean) 1 1 1
QueueAll.setPerson(int,int,PersonRequest) 1 4 4
QueueAll.subCount() 1 1 1
Thread1.Thread1(QueueAll) 1 1 1
Thread1.run() 3 5 6
Thread2.Thread2(QueueAll) 1 1 1
Thread2.changeUpDown() 1 1 3
Thread2.judgeFloor(int,int) 1 3 3
Thread2.moreThanOne(int) 1 4 4
Thread2.run() 1 7 8
 
Class OCavg WMC
ElevatorRun 1.75 21
Main 1 1
People 1 3
QueueAll 2.5 25
Thread1 2.5 5
Thread2 3.2 16
 

Bug分析

     这一阶段的作业主要的bug在主要是线程bug,这就是多线程的玄学之处,IDEA JAVA提供了线程断点的方法提供Debug,设置断点查看某一线程中程序是否如约执行。但是这一工具只是手段,最有效的方法是定位bug出没的区域并进行形式分析。

     第五次作业只要能写出来基本就是无bug的,因此本房间在互测中也是极其默契地保持和平。

     第六次作业以现实电梯的运行作为指导模型,是迄今为止的所有作业中最面向对象设计的一次,因此,模型建构了然以后就减少了出现考虑不周导致bug的风险。这种严格对标现实乘坐电梯的自然设计方法不容易引入bug,当然性能也好不到哪去,在强测中过了所有数据点但性能分整体中等偏下。这一次的互测屋又是一个平安夜。

心得体会

     第七次作业的惨痛教训教育我以后一定挤出时间尽早开始写作业,以便增加时间容错性,留出重构的回旋余地。及时调整好状态,迎接下一阶段传说中的出租车调度问题!

posted @ 2019-04-22 02:48  SkyWalkerSkyWalker  阅读(166)  评论(0编辑  收藏  举报