北航oo作业第二单元小结
类的设计:
首先,我对我的思路进行整体的说明,由于我的三次作业,思路是继承的,所以做总体的说明
第一, Main类,Main类自身并没有功能,他的功能只是构造需要的电梯线程和输入线程。
其中,第三次作业中,main类负责将电梯参数(运转时间,负载上限,运行楼层)传入Memory类和Elevator类
第二, 是Eleinput类,这个类,是一个单独的线程,功能是读入需求,每次读入需求,将其写入Memory类中,当读入null时进程结束。
输入器,与电梯类不相连,只负责传数据给Memory,设计安全。
第三, 是Memory类,Memory类内构造了一个线程安全的容器(阻塞对列,或concurrenthashmap),功能是:
存储需求,
反馈自身一部分状态(如是否有需求,以及传出需求),
删除已执行的需求,
在第三次作业里,还加入了对换乘的处理。
第四, 是Elevator类,每次从Memory与该电梯内部队列(即已经进入电梯的乘客)中选择一个主需求,执行该需求的过程中,逐层(如果能到达的话,判断上客,下客。
我的设计中,电梯无法直接访问需求队列,只能通过memory中的mainset方法和search方法,得到一个需求,这样设计安全,也能大大解耦。
第五, 是PersonRe类,这个类,是官方给的包的personrequest的拓展,在这个类中实现了,改写起始楼层的功能。在程序中,用该类,代替personrequest(第三次作业中使用。
时序图如下:
第一次作业
由于,第一次作业是先来先服务的傻瓜电梯,我并没有使用算法来优化电梯的时间。
在整体的设计思路上,设计了两个独立的线程,电梯线程与输入请求模拟器线程。在两个电梯的通讯中,由于首次设计多线程程序,并且第一次作业没有限制CPU时间,我选择了通过while循环暴力轮询的形式。
在两个线程之外,通过阻塞队列,设计了一个调度器,调度器,从输入请求模拟器中读入请求,并将其存入到调度器内的队列中,电梯通过while循环轮询调度器内是否有请求,一旦有请求就从阻塞队列中移出一个请求,并存入电梯内,循环这个过程即可。
最后,结束进程,通过两个条件判断,第一,在输入请求模拟器线程结束时,将调度器内内置的bool值设为true,当该值,与调度器内队列isempty的bool值都为true时,结束进程。
代码分析:
由类图可见,我的两个线程只是简单的一写一读。层次简单。
下面是我的代码度量分析:
这一系列的作业,我已经开始掌握了oo思想,没有出现上一系列的作业那种复杂度几十甚至上百的情况,不过run方法中,还是使用了过多的if判断,这一部分,可以选择分成三个小类,这样的话会是更好的选择。
2. 第二次作业
第二次作业是同向请求可稍带的“ALS电梯”。设计思路,与第一次作业有变化。首先,本次电梯,是需要考虑捎带的,而不是每次只服务第一位来的人。所以调度器内存储数据的容器不能继续使用阻塞队列,应该使用hashmap。在hashmap中,将需求到来排序的sequence作为key,需求作为value,这样也方便选择最先来的乘客进行服务。
此外,为了限制CPU时间避免暴力轮询,我使用了wait--notify让线程在不需运行的时候wait,具体的实现方法,是选择waitElevator类,每当Eleinput类,读入新的需求,便notifyall一次,最后,为了线程安全结束,当Eleinput类结束时,也notifyall一次。
最后,为了使电梯实现捎带功能,在电梯设计中,加入乘客处理类,每到一层后,先判断该层是否有人下,再结合电梯运行方向判断是否有乘客进入电梯,sleep开关门时间后,再判断一次是否可以捎带电梯(这一点十分重要,如果没有这一次判断,会使得可以上电梯的人无法被捎带,如果只有这一次判断,那么会导致每一层都需要开关门时间,尽管可能该层不需要停靠开关门)。
类图分析:
可以发现,代码架构变化不大,只是在电梯内加入了部分方法。
代码度量分析:
Method |
ev(G) |
iv(G) |
v(G) |
EleInput.EleInput(Memory) |
1 |
1 |
1 |
EleInput.run() |
3 |
3 |
3 |
Elevator.Elevator(Memory) |
1 |
1 |
1 |
Elevator.choosemain() |
4 |
3 |
4 |
Elevator.everyfloor(int,PersonRequest,int) |
1 |
5 |
5 |
Elevator.floordeal(int,int,int,int) |
1 |
2 |
5 |
Elevator.minus(int,int) |
2 |
1 |
2 |
Elevator.order(int,int,int) |
4 |
1 |
4 |
Elevator.pepolein(int,int,boolean) |
1 |
3 |
3 |
Elevator.pepoleout(int,int) |
1 |
4 |
4 |
Elevator.run() |
5 |
10 |
12 |
Elevator.searchele(PersonRequest) |
3 |
2 |
3 |
Elevator.sleeptime(long) |
1 |
2 |
2 |
Memory.Mainisset() |
2 |
1 |
2 |
Memory.Mainset() |
3 |
2 |
3 |
Memory.Memoryadd(PersonRequest) |
1 |
1 |
1 |
Memory.Memoryisempty() |
2 |
1 |
2 |
Memory.Search(Integer) |
1 |
1 |
1 |
Memory.Stopout() |
1 |
1 |
1 |
Memory.follow(int,boolean) |
5 |
4 |
5 |
Memory.order(int,int) |
2 |
1 |
2 |
Memory.setStop(boolean) |
1 |
1 |
1 |
Work.main(String[]) |
1 |
1 |
1 |
Class |
OCavg |
WMC |
|
EleInput |
2 |
4 |
|
Elevator |
3.91 |
43 |
|
Memory |
2 |
18 |
|
Work |
1 |
1 |
|
Package |
v(G)avg |
v(G)tot |
|
2.96 |
68 |
||
Module |
v(G)avg |
v(G)tot |
|
ElevatorPro |
2.96 |
68 |
|
Project |
v(G)avg |
v(G)tot |
|
project |
2.96 |
68 |
由于我基本继承了第一次作业的写法,Elevator中的run方法逻辑复杂度还是过高,order、choosemain等方法,也是因为if等判断过多,带来了逻辑复杂度过高,这是我程序设计的弊端。此外,本次作业中,由于run方法内,每层判断能否捎带乘客,带来了过高的模块设计复杂度和圈复杂度。在debug过程也因此耗费了很多精力。
3. 第三次作业
第三次作业,基本是在第二次作业的基础上完成,只做了为数不多的改动。
第一, 将personrequest拓展成一个新类,实现改变出发地点的功能。
第二, 为电梯添加负载人数等接口。
第三, 将调度器中的hashmap加锁。
第四, 在调度器中加入换乘类,计算请求是否需要换乘。
如果需要换乘(即三部电梯都不能独立完成,则将该电梯的转乘位设为由换乘类算出的最佳换乘楼层)。当有电梯到达该需求出发楼层,且能到达换乘楼层的就进行捎带。
第五, 在电梯内加入换乘功能。
判断乘车出电梯,加入换乘的情况,那么乘客到达换乘楼层,就出电梯,出电梯时,还要调Memory中一个特殊的方法,将这个需求处理后添加到请求队列中,(起始楼层更改,转乘位置为0),并notifyall一次。
类图:
本次类图中可以看出,加入了新类(数据类)PersonRe,该类,由Memory类处理完成。
度量分析:
Method |
ev(G) |
iv(G) |
v(G) |
Method |
1 |
1 |
1 |
Method |
3 |
3 |
3 |
Elevator.Elevator(Memory,String,List,long,long,long,int) |
1 |
1 |
1 |
Elevator.checkfloor(int) |
3 |
2 |
3 |
Elevator.choosemain(String,Boolean) |
9 |
6 |
9 |
Elevator.everyfloor(int,PersonRe,int) |
1 |
5 |
5 |
Elevator.floordeal(int,int,int,int) |
1 |
2 |
5 |
Elevator.order(int,int,int) |
4 |
1 |
4 |
Elevator.pepolein(int,int,boolean) |
3 |
4 |
5 |
Elevator.pepoleout(int,int) |
1 |
10 |
11 |
Elevator.run() |
7 |
11 |
13 |
Elevator.searchele(PersonRe) |
3 |
2 |
3 |
Elevator.sleeptime(long) |
1 |
2 |
2 |
Elevator.tofloor(PersonRe) |
4 |
3 |
4 |
Elevator.waituntil() |
3 |
3 |
4 |
Memory.EleTrans() |
2 |
1 |
2 |
Memory.Mainset(String) |
9 |
9 |
9 |
Memory.Memoryadd(PersonRequest) |
1 |
1 |
2 |
Memory.Memoryaddextra(PersonRe) |
1 |
1 |
1 |
Memory.Memoryisempty() |
2 |
1 |
2 |
Memory.Search(Integer) |
1 |
2 |
2 |
Memory.Stopout() |
1 |
1 |
1 |
Memory.check(String,PersonRe) |
1 |
3 |
3 |
Memory.checktrans(PersonRe) |
2 |
2 |
4 |
Memory.follow(int,boolean,String) |
6 |
4 |
6 |
Memory.listrtans(PersonRe,List) |
3 |
2 |
5 |
Memory.order(int,int) |
2 |
1 |
2 |
Memory.setStop(boolean) |
1 |
1 |
1 |
PersonRe.getDirty() |
1 |
1 |
1 |
PersonRe.getFromFloor() |
1 |
1 |
1 |
PersonRe.getPersonId() |
1 |
1 |
1 |
PersonRe.getToFloor() |
1 |
1 |
1 |
PersonRe.getTrans() |
1 |
1 |
1 |
PersonRe.resetDirty() |
1 |
1 |
1 |
PersonRe.setDirty() |
1 |
1 |
1 |
PersonRe.setFrom(int) |
1 |
1 |
1 |
PersonRe.setMemory(PersonRequest) |
1 |
1 |
1 |
PersonRe.setTo(int) |
1 |
1 |
1 |
PersonRe.setTrans(int) |
1 |
1 |
1 |
Work.main(String[]) |
1 |
1 |
1 |
Class |
OCavg |
WMC |
|
EleInput |
2 |
4 |
|
Elevator |
5.15 |
67 |
|
Memory |
2.92 |
38 |
|
PersonRe |
1 |
11 |
|
Work |
1 |
1 |
|
Package |
v(G)avg |
v(G)tot |
|
3.12 |
125 |
||
Module |
v(G)avg |
v(G)tot |
|
ElevatorPlus |
3.12 |
125 |
|
Project |
v(G)avg |
v(G)tot |
|
project |
3.12 |
125 |
其中,逻辑复杂度,是和前两次作业一样由if判断导致。而在pepoleout类和mainset类中的模块设计复杂度增加的原因,是我加入了换乘。我的换乘实现方式需要Memory和Elevator的协作处理,导致该复杂度增加。至于处理方法,可以添加一个新的类,在将调度器的存储需求队列的功能分离,可以解耦。
自己程序的bug:
我只在第三次作业出现了bug,该bug来自于在程序中使用固定值,进行for循环,由于第二次电梯最多30条指令,我将该固定值设计为32,在第三次电梯中沿用了第二次电梯的设计,导致,我的电梯,无法处理32号之后的指令。
该结构来自于结构的不合理,结构中应避免出现魔术数字。
发现别人的bug
本次系列课程中,我未能发现别人的bug,但我采取的策略是构造测试样例,并带入检验。
构造测试样例,重点考察,换乘情况,一上一下,以及大量人在同一楼层同时进入。
心得体会:
第一,在程序设计中,一定要避免magic number的引用!我第三次作业就是血泪的教训。
第二,方法一定要简洁,一个方法只做一件事,这样能降低逻辑复杂度,降低debug难度。
第三,要使用wait和notify机制,用好这个机制,可以减少cpu时间,写出巧妙的代码,(跑测试样例时,也不会像暴力轮询那样,风扇狂转)
第四,多花时间进行架构的设计,本次电梯作业中,如果第一次就做出了非常漂亮的架构,整个扩展过程都会舒服很多,程序的稳定性也会更佳。
第五,在synchronized的运用,要多加思考,用少了会影响正确性,用多了又影响性能,需要精雕细琢。
北航oo作业第二单元小结的更多相关文章
- 北航oo作业第一单元小结
前言 在经过了三次艰辛的oo作业后,oo课程的第一单元告一段落,这一单元,我作为一个oo小白,开始了解oo的编程思想,也有了自己的一点心得体会.把笔粗成字,不当之处,还请各位大佬多多指教. 一.分析程 ...
- 北航OO(2020)第二单元博客作业
第二单元第一次作业 多线程设计策略 第一次作业的想法是设计三个线程:输入线程,调度器线程以及电梯线程.输入线程获取请求并发送给调度器线程:调度器线程通过查询电梯线程的状态(等待.停靠以及移动),并综合 ...
- 2019北航oo课程第二单元作业总结..#_#..
学习了之前在写代码是从来没有见过的多线程之后,便迎来了此次电梯作业.说实话,这次作业做得十分的辛苦,虽然在前三次作业中领悟到了java面向对象的精髓,但是再加上了多线程之后,又开始理不清思路,对自己的 ...
- OO作业第二单元总结
目录 一.设计策略 1 2 3 二.程序分析 1 2 3 S.O.L.I.D分析 三.Bug分析 1 2 3 四.互测策略 五.心得体会 一.设计策略 1 第一次完成的是一个傻瓜电梯,简单来说,就是来 ...
- OO第二单元小结
OO第二单元小结 一.三次作业代码分析. 1.第一次作业 第一次作业是单部电梯的傻瓜调度,由于其过分傻瓜,所以第一次作业我只有两个类,一个main,一个电梯,main类负责不断从输入流中读取命令,如果 ...
- 2019年北航OO第1单元(表达式求导)总结
2019年北航OO第1单元(表达式求导)总结 1 基于度量的程序结构分析 量化指标及分析 以下是三次作业的量化指标统计: 关于图中指标在这里简要介绍一下: ev(G):基本复杂度,用来衡量程序非结构化 ...
- 2020北航OO第四单元总结
2020北航OO第四单元总结 一.本单元架构设计 本单元作业是实现一个UML图解析器,其中实现接口及主要框架课程组已经提供,只需要我们完成特定功能. 在第一次作业时,感到十分迷茫,不知道如何下手,最后 ...
- 2020北航OO第三单元总结
2020北航OO第三单元总结 本单元要求是根据JML规格完善代码,初看是一个简单的代码照搬实现的东西,但最后才发现由于CPU时间的限制,还考察了大量优化策略及数据结构中关于图的知识,是一次非常注重细节 ...
- 北航OO第四单元——UML图解析
北航OO第四单元--UML图解析 作业要求简析 刚接触本次作业可能需要花上一会才能搞清楚到底是要我们写个啥,在这里简单说一下: UML图的保存格式.mdj文件是以json文件的形式存储的,将每一个Um ...
随机推荐
- 第四周作业-视频学习、教材作业wireshark
教材总结与作业 总结 网络嗅探技术定义:网络嗅探(sniff)是一种黑客常用的窃听技术,(1)利用计算机的网络接口截获目的地为其他计算机的数据报文,(2)监听数据流中所包含的用户账户密码或私密通信等. ...
- vim 设置TAB宽度、显示行号、自动缩进、自动换行宽度
一.vim ~/.vimrc 二.添加如下几行:(括号中的不是,是我添加的) set shiftwidth=4 (表示每一级缩进的长度)set softtabstop=4 ...
- C# Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
一.问题描述 在做C# 的 Guid 转换时,出现这个问题:Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-x ...
- SpringBoot第六篇:整合通用Mapper
作者:追梦1819 原文:https://www.cnblogs.com/yanfei1819/p/10876339.html 版权声明:本文为博主原创文章,转载请附上博文链接! 引言 在以往的项 ...
- BZOJ 3211【线段树】
题意: n个数,m个操作. 1,L,R 询问[L , R] 的总和. 2,L,R 将区间所有数都开根号. 思路: 区间和简单. 主要就是一个 区间所有元素相同的标记Same ,但是这样是不是要求太 ...
- uva11491 奖品的价值(贪心)
uva11491 奖品的价值(贪心) 给你一个n位的整数,请你删除其中的d个数字,使得整数尽可能大.1<=d<n<=1e5. 首先因为前面的数位更重要,所以从左往右将每一位数字加入栈 ...
- angularjs 使用angular-sortable-view实现拖拽效果(包括拖动完成后的方法使用)
首先还是看效果图吧,方便大家可以快速得知是否是自己需要的功能:(抱歉电脑还未安装动图软件,先用.png) 如果上图是你需要的功能效果图,那么请往下看,我有写出来例子哦~ 使用这个插件有几个好处,首先: ...
- Trie UVALive 7192 Chip Factory (15长春J)
题目传送门 题意:从n个数中选出不同的三个数a b c,使得(a+b)^c 最大 分析:先将所有数字按位插入到字典树上,然后删除两个数字,贪心询问与剩下的数字最大异或值. /************* ...
- onbeforeunload与onunload事件
Onunload,onbeforeunload都是在刷新或关闭时调用,可以在<script>脚本中通过 window.onunload来指定或者在<body>里指定.区别在于o ...
- svn常用功能使用简介
1.文档库地址: https://xxx.xxx.xxx.xxx/svn/ 2.svn添加文件 2.1 在本地电脑上任何空白地方,右键-->打开“浏览版本库(Repo-browser)”,如图: ...