BUAAOO 第二单元作业总结
单元综述
单元总共为三次作业,难度递增。
-
第一次:完成的任务为单部多线程傻瓜调度(FAFS)电梯的模拟。
-
第二次:需要完成的任务为单部多线程可捎带调度(ALS)电梯的模拟。
-
第三次:需要完成的任务为多部多线程智能(SS)调度电梯的模拟。
通过这三次作业,我逐渐完成了一个多线程电梯系统,对于线程也有了更深的理解。
一. 基于度量的程序结构分析
本文中基于度量分析的图片采用IDEA的Metrics生成。
分析结果解释的方法来源于博客(https://www.cnblogs.com/qianmianyu/p/8698557.html)
ev(G):即Essentail Complexity,用来表示一个方法的结构化程度,范围在之间,值越大则程序的结构越“病态”,其计算过程和图的“缩点”有关。
iv(G):即Design Complexity,用来表示一个方法和他所调用的其他方法的紧密程度,范围也在之间,值越大联系越紧密。
v(G):即循环复杂度,可以理解为穷尽程序流程每一条路径所需要的试验次数。
对于类,有OCavg和WMC两个项目,分别代表类的方法的平均循环复杂度和总循环复杂度。
第一次作业
类图
复杂度分析表
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
Elevator.Elevator(RequestList,int) | 1 | 1 | 1 |
Elevator.close(int) | 1 | 1 | 1 |
Elevator.in(int,int) | 1 | 1 | 1 |
Elevator.open(int) | 1 | 1 | 1 |
Elevator.out(int,int) | 1 | 1 | 1 |
Elevator.run() | 1 | 2 | 2 |
Elevator.sleep(int) | 1 | 2 | 2 |
Input.Input(RequestList) | 1 | 1 | 1 |
Input.run() | 3 | 3 | 4 |
Main.main(String[]) | 1 | 1 | 1 |
Request.Request(PersonRequest) | 1 | 1 | 1 |
Request.Request(int,int,int) | 1 | 1 | 1 |
Request.getFromFloor() | 1 | 1 | 1 |
Request.getPersonId() | 1 | 1 | 1 |
Request.getToFloor() | 1 | 1 | 1 |
RequestList.get() | 1 | 2 | 3 |
RequestList.getFirst() | 1 | 1 | 1 |
RequestList.getSize() | 1 | 1 | 1 |
RequestList.put(Request) | 1 | 2 | 3 |
Class | OCavg | WMC |
---|---|---|
Elevator | 1.14 | 8 |
Input | 2 | 4 |
Main | 1 | 1 |
Request | 1 | 5 |
RequestList | 1.5 | 6 |
分析
在第一次作业中,我有两个仓库,分别是insideList(List中是电梯里面的请求),outsideList(List中是电梯外面,即还没有被电梯取走的请求)。Input线程向仓库outsideList里里面放东西,Elevator从仓库outsideList里面取东西。Elevator将从outsideList中取出来的指令放到自己的insideList里,指令执行完毕(即人从电梯中出去之后)将指令从insideList中取出。
第一次调度方法我直接采用的傻瓜调度,将指令一个一个执行。每一个Method的复杂度都不是很高,整体架构还可以。
第二次作业
类图
复杂度分析表
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
Elevator.Elevator(InsideList,OutsideList,int) | 1 | 1 | 1 |
Elevator.arrive(int) | 1 | 1 | 1 |
Elevator.close(int) | 1 | 1 | 1 |
Elevator.getDir(int,int) | 2 | 1 | 2 |
Elevator.getFloorNow() | 1 | 1 | 1 |
Elevator.getNextDir(Request) | 7 | 10 | 10 |
Elevator.in(int,int) | 1 | 1 | 1 |
Elevator.middle(int,int) | 1 | 11 | 13 |
Elevator.open(int) | 1 | 1 | 1 |
Elevator.out(int,int) | 1 | 1 | 1 |
Elevator.printBeside() | 1 | 7 | 7 |
Elevator.printIn(Request) | 1 | 2 | 2 |
Elevator.printOut(Request) | 1 | 2 | 2 |
Elevator.run() | 1 | 6 | 6 |
Elevator.sleep(int) | 1 | 2 | 2 |
Input.Input(OutsideList) | 1 | 1 | 1 |
Input.run() | 3 | 3 | 4 |
InsideList.get(int) | 1 | 3 | 4 |
InsideList.getEle(int) | 1 | 1 | 1 |
InsideList.getOutPeople(int) | 3 | 2 | 3 |
InsideList.getSize() | 1 | 1 | 1 |
InsideList.isEmpty() | 2 | 1 | 2 |
InsideList.put(Request) | 1 | 2 | 3 |
Main.main(String[]) | 1 | 1 | 1 |
OutsideList.get(int) | 2 | 3 | 4 |
OutsideList.getDownPick(int) | 3 | 2 | 4 |
OutsideList.getEle(int) | 1 | 1 | 1 |
OutsideList.getSize() | 1 | 1 | 1 |
OutsideList.getUpPick(int) | 3 | 2 | 4 |
OutsideList.isEmpty() | 2 | 1 | 2 |
OutsideList.put(Request) | 1 | 2 | 3 |
Request.Request(PersonRequest) | 1 | 1 | 2 |
Request.Request(int,int,int) | 1 | 1 | 1 |
Request.getDir() | 1 | 1 | 1 |
Request.getFromFloor() | 1 | 1 | 1 |
Request.getPersonId() | 1 | 1 | 1 |
Request.getToFloor() | 1 | 1 | 1 |
Class | OCavg | WMC |
---|---|---|
Elevator | 3 | 45 |
Input | 2 | 4 |
InsideList | 1.83 | 11 |
Main | 1 | 1 |
OutsideList | 2 | 14 |
Request | 1.17 | 7 |
分析
这次我还是沿用了第一次作业的思路,设置了insideList和outsideList两个仓库。整体采用ALS的想法,并且在电梯运行中间加入了捎带人的操作。
这次Elevator.middle(int,int) 和 Elevator.getNextDir(Request) 的循环复杂度和设计复杂度较高。这两个方法一个是处理接人的问题,一个是处理电梯下一个运行方向的问题。因此执行次数可能比较多。
第三次作业
类图
-
整体
-
package : elevator
-
package : input
-
package : requestList
复杂度分析表
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
Main.main(String[]) | 1 | 1 | 1 |
elevator.Elevator.Elevator(InsideList,OutsideList,FinalList,int,int[],int,char,int,int) | 1 | 2 | 2 |
elevator.Elevator.arrive(int) | 1 | 1 | 1 |
elevator.Elevator.close(int) | 1 | 1 | 1 |
elevator.Elevator.getDir(int,int) | 2 | 1 | 2 |
elevator.Elevator.getOutSize() | 1 | 1 | 1 |
elevator.Elevator.in(int,int) | 1 | 1 | 1 |
elevator.Elevator.insideOut(Request) | 1 | 2 | 2 |
elevator.Elevator.middle() | 1 | 3 | 3 |
elevator.Elevator.middleDown() | 3 | 12 | 15 |
elevator.Elevator.middleUp() | 3 | 12 | 15 |
elevator.Elevator.open(int) | 1 | 1 | 1 |
elevator.Elevator.out(int,int) | 1 | 1 | 1 |
elevator.Elevator.printBeside() | 1 | 17 | 19 |
elevator.Elevator.printIn(Request) | 1 | 1 | 1 |
elevator.Elevator.printOut() | 1 | 1 | 1 |
elevator.Elevator.run() | 1 | 8 | 8 |
elevator.Elevator.sleep(int) | 1 | 2 | 2 |
input.Input.Input(OutsideList,FinalList) | 1 | 1 | 1 |
input.Input.run() | 3 | 3 | 4 |
requestlist.FinalList.get(Request) | 2 | 3 | 4 |
requestlist.FinalList.getIndex(Request) | 3 | 2 | 3 |
requestlist.FinalList.put(Request) | 1 | 2 | 3 |
requestlist.FinalList.size() | 1 | 1 | 1 |
requestlist.FinalList.waiting(Request) | 1 | 3 | 4 |
requestlist.InsideList.get(int) | 1 | 3 | 4 |
requestlist.InsideList.getEle(int) | 1 | 1 | 1 |
requestlist.InsideList.getFirstIndex(char) | 3 | 2 | 3 |
requestlist.InsideList.getNotRemove() | 2 | 2 | 3 |
requestlist.InsideList.getOutPeople(int) | 3 | 2 | 3 |
requestlist.InsideList.getSize() | 1 | 1 | 1 |
requestlist.InsideList.haveDown() | 1 | 2 | 3 |
requestlist.InsideList.haveOut(int) | 3 | 2 | 3 |
requestlist.InsideList.haveUp() | 1 | 2 | 3 |
requestlist.InsideList.isEmpty() | 2 | 1 | 2 |
requestlist.InsideList.put(Request) | 1 | 2 | 3 |
requestlist.OutsideList.get(int) | 1 | 1 | 1 |
requestlist.OutsideList.get(int,char) | 2 | 3 | 4 |
requestlist.OutsideList.getDownPick(int,char) | 3 | 4 | 5 |
requestlist.OutsideList.getEle(int) | 1 | 1 | 1 |
requestlist.OutsideList.getFirstIndex(char) | 3 | 2 | 3 |
requestlist.OutsideList.getNotRemove(char) | 2 | 3 | 4 |
requestlist.OutsideList.getSize(char) | 1 | 2 | 3 |
requestlist.OutsideList.getUpPick(int,char) | 3 | 4 | 5 |
requestlist.OutsideList.isEmpty() | 2 | 1 | 2 |
requestlist.OutsideList.peopleAbove(int,char) | 1 | 4 | 5 |
requestlist.OutsideList.peopleBelow(int,char) | 1 | 4 | 5 |
requestlist.OutsideList.printId() | 1 | 2 | 2 |
requestlist.OutsideList.put(Request) | 1 | 7 | 8 |
requestlist.OutsideList.size() | 1 | 1 | 1 |
requestlist.Request.Request(PersonRequest) | 1 | 1 | 3 |
requestlist.Request.Request(int,int,int,char) | 1 | 1 | 1 |
requestlist.Request.getDir() | 1 | 1 | 1 |
requestlist.Request.getDirect() | 1 | 1 | 1 |
requestlist.Request.getFromFloor() | 1 | 1 | 1 |
requestlist.Request.getPersonId() | 1 | 1 | 1 |
requestlist.Request.getToFloor() | 1 | 1 | 1 |
requestlist.Request.getType() | 1 | 1 | 1 |
requestlist.Request.getType2() | 1 | 1 | 1 |
requestlist.Request.judgeDirect(int,int,int) | 1 | 7 | 36 |
requestlist.Request.reManager() | 2 | 5 | 10 |
requestlist.Request.transfer() | 1 | 1 | 1 |
requestlist.Request.twoDirect() | 1 | 1 | 1 |
Class | OCavg | WMC |
---|---|---|
Main | 1 | 1 |
elevator.Elevator | 2.88 | 49 |
input.Input | 2 | 4 |
requestlist.FinalList | 2 | 10 |
requestlist.InsideList | 2.27 | 25 |
requestlist.OutsideList | 2.29 | 32 |
requestlist.Request | 2.31 | 30 |
分析
第三次作业中我的电梯沿用了之前的思想,不过由于是多电梯,总体设置了一个outsideList作为总请求仓库,每个电梯都分别设置了insideList作为电梯内部的请求仓库。
我的电梯总体采用的是Look算法,根据具体情况加了一些改动。我的电梯采用的是自己抢人的模式,即电梯在运行途中发现可以捎带的人,就把请求从outsideList 中取出放入自己的insideList中。先对指令进行处理,限定人可以乘坐的一个或几个电梯,之后就是电梯自由抢,哪个电梯先到就让人上哪个电梯。
这次的 eV(G)还都比较正常,不过有些方法的 iV(G)比较高。
二. 分析自己程序的bug
第一次作业
第一次作业,大概由于写的算法比较简单,而且之前测试的也比较充分,并没有出现什么bug。
第二次作业
第二次作业中,我也没有出现bug。
第三次作业
第三次作业中,我的程序出现了REAL_TIME_LIMIT_EXCEED的错误。我的电梯没有结束是因为我的电梯无法正确处理三层到1-15层的非直达现象。我当时分了三部分考虑换乘,在中间的这一部分中,我只考虑了三层到2,4层和2,4层到三层的换乘行为,没有考虑其他层。由于无法处理,就没有办法把这一项从outsideList里取出,导致了程序无法正确结束的情况。而且我的程序在Ctrl D输入较晚时可能会产生超时现象,因为在中间的时候没有考虑没输入结束符但outsideList已经为空的情况。
这次的错误可以说完全是因为自己测试不够充分造成的,起始这个错误在随机多数据输入的时候就会出现,下次一定应该注意测试的充分性。
三. 互测DEBUG
在每一次作业的互测中,我的样例主要来源于两个方面。一是来源于我自己在编程序的过程中没有想到的点,也就是在debug过程中发现的问题。二是来源于在互测过程中构造的样例。
样例的构造
在前两次作业中,我有许多互测用例的构造都基于对于特殊情况的处理。比如电梯需要改方向时能否正确处理,以及开门时可以多人上下的情况处理
四. Applying Creational Pattern
在课上老师的讲解过程中,我知道了调度器的妙用。我在这三次作业中都没有用到调度器,感觉可能在可扩展性方面会稍微弱一些,如果需要改调度方法就需要在电梯类中直接进行修改。我觉得下次如果有机会,我觉得可以尝试调度器模式。
而且在我在每次捎带人的时候,都需要对List中的请求进行遍历,课上有讲过一种方法,给每个电梯直接构造一个接人表,我感觉这样可能会更加清楚一些。
五. 总结
在这一单元中,从刚开始对于多线程一知半解,到后来可以写出多电梯,在这一过程中,我对于多线程有了更加深刻了理解。我也要吸取最后一次的教训,还是要更加仔细的测试自己的代码,争取在下课单元中能有所进步吧。