OO作业第二单元总结
一、设计策略
1
第一次完成的是一个傻瓜电梯,简单来说,就是来一个请求,就服务一个请求,服务完之后再服务下一个请求,这样循环往复。在完成这一次作业的时候,我对于多线程的理解还不是很深入,一开始在尝试单开一个电梯线程完成这次作业的时候出现了一些bug,我便尝试继续使用单线程的思路,很快就写出了单线程版本的作业,简单来说就是将电梯的所有功能写到一个deal函数中:
- public void deal(PersonRequest r) throws Exception {
- long dis;
- dis = Math.abs(floorn - r.getFromFloor());
- Thread.sleep(dis * runt);
- floorn = r.getFromFloor();
- TimableOutput.println(String.format("OPEN-%d", floorn));
- Thread.sleep(opent);
- TimableOutput.println(
- String.format("IN-%d-%d", r.getPersonId(), floorn));
- Thread.sleep(closet);
- TimableOutput.println(
- String.format("CLOSE-%d", floorn));
- dis = Math.abs(floorn - r.getToFloor());
- Thread.sleep(dis * runt);
- floorn = r.getToFloor();
- TimableOutput.println(String.format("OPEN-%d", floorn));
- Thread.sleep(opent);
- TimableOutput.println(
- String.format("OUT-%d-%d", r.getPersonId(), floorn));
- Thread.sleep(closet);
- TimableOutput.println(String.format("CLOSE-%d", floorn));
- }
在主函数中建立while循环,取到请求之后就调用这个电梯函数进行服务,然后循环往复,直到取到null之后跳出while循环,结束主函数。这样一个单线程版本实现很简单并且不容易出bug,唯一利用到多线程思想的地方是在电梯的deal函数中为了实现等待运行时间调用了Thread.sleep方法。通过所有中测之后,我又继续完成了自己的多线程版本,大体采用的是生产者-消费者模式,虽然也通过了中测,但是总体设计比较混乱,在这里就不做分析了。
2
第二次完成的是一个可捎带的电梯,我的主要思路如下:主线程充当输入线程,再开一个调度器线程,一个电梯线程,输入线程和调度器线程共享一个请求队列A,调度器线程和电梯线程共享一个请求队列B,电梯内部独享一个捎带请求队列C,输入线程要做的事情就是不断取得请求,加入队列A;调度器线程这次只有一个电梯可以指挥,故它要做的事情就是不断从队列A中取出请求加入队列B;而电梯线程要做的事情就是不断从队列B中取出请求进行服务,而具体的服务流程是这样的,首先从B中拿出一个请求作为主请求,然后在完成这个主请求的过程中,每到一层就调用check函数:检查有没有人可以进电梯(要去的楼层和我当前的运行方向相同),有没有人可以出电梯(到达了他的目标楼层),可以进的就加入队列C,可以出的就从队列C中删除掉,最后在完成我的主请求之后,检查队列C是否为空,如果不为空,利用pop函数:不断取队首的请求对象进行服务,直到为空,再次进行while循环,重新取一个主请求……循环往复。
3
这一次的作业实现的是同时管理三部电梯,而且这三部电梯在性能(能够到达的楼层、容量、运行速度)上都有着区别,但我个人在完成这次作业的过程中感觉其实在对于线程的处理并没有什么新的内容,仍然是调度器线程完成分配,电梯线程负责跑,主线程负责输入,各个线程的运行保持独立,在共享资源的访问上避免冲突,与第二次作业没有很大的区别,但当然也不是完全没有区别,在我的设计之中,主要区别有二:
1.本次的作业需要将某些请求拆分,分配给两个电梯完成,拆分出来的两个请求,第一个直接分配给电梯,第二个存在调度器内部的wlist中,等待电梯完成第一部分请求后再进行二次分配,
- public void dis2(Request t, int f, Elevator e1, Elevator e2) {
- Request t1 = new Request(t.getFromFloor(), f, t.getPersonId());
- Request t2 = new Request(f, t.getToFloor(), t.getPersonId());
- t1.setCon(1);
- t2.setEle(e2.getName());
- e1.getLista().add(t1);
- wlist.add(t2);
- }
这就需要电梯给调度器传递一个信息,告知调度器:我已完成第一部分任务,你可进行二次分配了。
- public void checkt(Request t) {
- if (t.getCon() == 0 || t.getCon() == 2) {
- return;
- } else {
- manager.trans(t.getPersonId());
- return;
- }
- }
2.在结束程序问题上,我的第二次和第三次作业也有着较大的区别。我的第二次作业中是这样做的:主线程(输入线程)在读到null之后调用manager.end()通知调度器:你结束手头上的工作之后就可以结束了,然后自己结束;调度器在while循环的开头进行检查,如果可以结束,并且已经将输入队列中的请求全部分配出去,就调用elevator.end()通知电梯(只有一部):你结束手头上的工作之后就可以结束了,然后自己结束;电梯线程同样在while循环的开头进行检查,如果可以结束,并且已经将请求队列中的所有请求服务完毕,就自己结束,调用systemexit(0),结束整个程序。而第三次作业显然就不能这样做,三部电梯处于平等地位,不能有哪一部拥有结束程序的权力,所以我采用方式是调度器在没有新请求,并且wlist为空之后通知各个电梯,电梯接到通知之后自身检查,发现已经完成所有任务之后再次通知调度器(调度器中的over变量自增),调度器在接到三部电梯各自的结束信号之后结束整个程序。
- this.elevator1.endIn();
- this.elevator2.endIn();
- this.elevator3.endIn();
- while (over != 3) {
- try {
- Thread.sleep(1);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- System.exit(0);
二、程序分析
1
类图:
这次作业我就只有两个类,main类负责接受输入,然后调用elevator类的deal方法进行服务。比较清晰。
规模:
复杂度:
Complexity metrics | 星期一 | 22 四月 2019 21:28:40 CST | |
---|---|---|---|
Method | ev(G) | iv(G) | v(G) |
Elevator.deal(PersonRequest) | 1 | 1 | 1 |
Main.main(String[]) | 3 | 4 | 4 |
Class | OCavg | WMC | |
Elevator | 1 | 1 | |
Main | 3 | 3 |
2
类图:
这次作业我有四个类,main类负责接受输入,requestlist是我自己实现的一个线程安全的请求队列,其中的get方法就是从队列头取元素并删除(先来先服务),add方法就是将新的请求加入队尾。manager类进行调度,elevator进行服务。逻辑结构比较清晰,但是各个线程之间是通过引用的方式进行通信的,其实还有更好的方法可以采用。
规模:
复杂度:
Complexity metrics | 星期一 | 22 四月 2019 21:29:42 CST | |
---|---|---|---|
Method | ev(G) | iv(G) | v(G) |
Elevator.check() | 1 | 1 | 1 |
Elevator.checkin() | 1 | 10 | 10 |
Elevator.checkout() | 1 | 4 | 4 |
Elevator.endIn() | 1 | 1 | 1 |
Elevator.getLista() | 1 | 1 | 1 |
Elevator.mainrun(PersonRequest) | 5 | 3 | 5 |
Elevator.mfcheck() | 1 | 6 | 6 |
Elevator.move() | 3 | 3 | 5 |
Elevator.popn() | 3 | 2 | 3 |
Elevator.run() | 3 | 9 | 10 |
Elevator.samedir(int,int) | 3 | 1 | 5 |
Main.main(String[]) | 3 | 3 | 4 |
Manager.endIn() | 1 | 1 | 1 |
Manager.run() | 3 | 6 | 7 |
Manager.setElevator(Elevator) | 1 | 1 | 1 |
Manager.setInlist(RequestList) | 1 | 1 | 1 |
Manager.setRulist(RequestList) | 1 | 1 | 1 |
RequestList.add(PersonRequest) | 1 | 1 | 1 |
RequestList.copy() | 1 | 1 | 1 |
RequestList.get() | 1 | 3 | 3 |
RequestList.getList() | 1 | 1 | 1 |
RequestList.isempty() | 2 | 1 | 2 |
RequestList.remove(PersonRequest) | 1 | 1 | 1 |
Class | OCavg | WMC | |
Elevator | 3.73 | 41 | |
Main | 3 | 3 | |
Manager | 1.6 | 8 | |
RequestList | 1.33 | 8 |
时序图:
3
类图:
为了保证输出线程安全创建了Out类,其他和第二次类似。
规模:
复杂度:
Complexity metrics | 星期一 | 22 四月 2019 21:30:40 CST | |
---|---|---|---|
Method | ev(G) | iv(G) | v(G) |
Elevator.Elevator(char,int,int,int,int[],Out) | 1 | 1 | 1 |
Elevator.able(Request) | 2 | 2 | 3 |
Elevator.can() | 2 | 1 | 2 |
Elevator.check() | 1 | 1 | 1 |
Elevator.checkin() | 1 | 12 | 12 |
Elevator.checkout() | 1 | 4 | 4 |
Elevator.checkt(Request) | 2 | 3 | 3 |
Elevator.chosem(RequestList) | 1 | 2 | 3 |
Elevator.contain(int[],int) | 3 | 1 | 3 |
Elevator.endIn() | 1 | 1 | 1 |
Elevator.getFable() | 1 | 1 | 1 |
Elevator.getLista() | 1 | 1 | 1 |
Elevator.getName() | 1 | 1 | 1 |
Elevator.getNum() | 1 | 1 | 1 |
Elevator.getNuma() | 1 | 1 | 1 |
Elevator.mainrun(Request) | 5 | 3 | 5 |
Elevator.mfcheck() | 1 | 7 | 7 |
Elevator.move() | 3 | 3 | 5 |
Elevator.popn() | 3 | 2 | 3 |
Elevator.run() | 3 | 9 | 10 |
Elevator.samedir(int,int) | 3 | 1 | 5 |
Elevator.setM(Manager) | 1 | 1 | 1 |
Main.main(String[]) | 3 | 3 | 4 |
Manager.Manager(Elevator,Elevator,Elevator,RequestList) | 1 | 1 | 1 |
Manager.coable(Request,int[],int[],int[]) | 2 | 3 | 3 |
Manager.contain(int[],int) | 3 | 1 | 3 |
Manager.dis2(Request,int,Elevator,Elevator) | 1 | 1 | 1 |
Manager.dispatch(Request) | 1 | 10 | 13 |
Manager.eleOver() | 1 | 1 | 1 |
Manager.endIn() | 1 | 1 | 1 |
Manager.isb(int,int,int) | 3 | 1 | 5 |
Manager.mid(int,int,int[]) | 3 | 2 | 3 |
Manager.run() | 3 | 11 | 11 |
Manager.share(Elevator,Elevator) | 3 | 1 | 9 |
Manager.trans(int) | 4 | 4 | 7 |
Out.print(String) | 1 | 1 | 1 |
Request.Request(int,int,int) | 1 | 1 | 1 |
Request.getCon() | 1 | 1 | 1 |
Request.getEle() | 1 | 1 | 1 |
Request.getFromFloor() | 1 | 1 | 1 |
Request.getPersonId() | 1 | 1 | 1 |
Request.getToFloor() | 1 | 1 | 1 |
Request.setCon(int) | 1 | 1 | 1 |
Request.setEle(char) | 1 | 1 | 1 |
RequestList.add(Request) | 1 | 1 | 1 |
RequestList.copy() | 1 | 1 | 1 |
RequestList.get() | 1 | 3 | 3 |
RequestList.getList() | 1 | 1 | 1 |
RequestList.isempty() | 2 | 1 | 2 |
RequestList.remove(Request) | 1 | 1 | 1 |
RequestList.size() | 1 | 1 | 1 |
Class | OCavg | WMC | |
Elevator | 2.68 | 59 | |
Main | 3 | 3 | |
Manager | 3.67 | 44 | |
Out | 1 | 1 | |
Request | 1 | 8 | |
RequestList | 1.29 | 9 |
时序图:
S.O.L.I.D分析
单一责任原则:调度器只管调度,电梯只负责运行,一二次作业没有问题,第三次作业中,由于有的请求需要拆分,导致电梯需要判断某个刚刚出门的乘客是不是所谓“第一步请求”,如果是的话需要通知调度器,一定程度上违背了该原则。
开放封闭原则:第二次作业中没有考虑到第三次会出现拆分等要求,所以可扩展性较差;第三次作业进行了完善 ,可以轻松地扩展出新型电梯。
里氏替换原则:没有类的父子关系。不违背里氏替换原则(LSP)原则。
接口分离原则:仅使用了Runnable一个接口。不违背分离原则(ISP)原则。
依赖倒置原则:高层模块“调度器”对于不同的底层模块“电梯”都能够同样地执行。符合依赖倒置原则(DIP)原则。
三、Bug分析
1
公测和互测未发现bug
2
公测和互测未发现bug
3
公测发现一个bug,表现为电梯超载,原因在于,我设计的电梯的每一次运行之前都要选择一个主请求,在完成主请求的过程中进行捎带,而虽然我在每一次试图捎带之前都判断了容量以确定是否可以捎带,但在主请求人员进入电梯之前我没有进行判断,所以导致某一次之中,在主请求人员进入电梯之前,即电梯前往主请求人员出发楼层的运行过程中,我的电梯已经捎带上了足够多的人导致电梯满了,这时主请求直接进入,导致电梯超载。解决方法是直接将各电梯的理论容量减一,这样就可以保证始终给我的主请求留出空间。
四、互测策略
这一单元的互测其实感觉是比较困难的,基本每次都是一个“尝试阅读代码找bug-找不出来-直接提交测试数据盲狙”的过程,只有在第二次作业的时候侥幸发现了一位同学的Bug,通过阅读他的代码,我发现他的电梯在完成主请求的第二阶段任务时,忘了将可以被捎带的请求从请求队列中删除,导致他的电梯在某种情况下会出现一个人两次进入电梯的情况。其他两次互测均未能发现Bug。
五、心得体会
1.架构最重要。
我的第一次电梯作业由于理解不到位写的非常混乱,导致我的第二次作业从头做起,耗费了相当大的精力,但是也正是因为这样,我的第二次作业最后产生的架构就比较好,一方面它比较安全,不会出现死锁、访问冲突、超时等各种问题;另一方面它十分清晰明了,这给我的第三次作业带来了巨大的便利。
2.最优可能-平均优化。
在第二次第三次的电梯作业的优化过程中,我发现:几乎没有哪一种算法是可以保证我在任何情况下的运行时间都最小的,因此我们的优化只能做到对于大多数的运行过程有一个较好的表现,在平均时间上占有优势,这其实也符合一般生活中的规律。
OO作业第二单元总结的更多相关文章
- 北航oo作业第二单元小结
类的设计: 首先,我对我的思路进行整体的说明,由于我的三次作业,思路是继承的,所以做总体的说明 第一, Main类,Main类自身并没有功能,他的功能只是构造需要的电梯线程和输入线程. 其中,第三 ...
- BUAA OO 2019 第二单元作业总结
目录 总 架构 controller model view 优化算法 Look 算法 多种算法取优 预测未来 多线程 第五次作业 第六次作业 第七次作业 代码静态分析 UML 类图 类复杂度 类总代码 ...
- OO第二次博客作业--第二单元总结
第一次作业 1. 设计策略 第一次作业,一共三个线程,主线程.输入线程和电梯线程,有一个共享对象--调度器(队列). 调度的策略大多集中到了电梯里,调度器反而只剩下一个队列. 2. 基于度量的分析 类 ...
- 北航OO(2020)第二单元博客作业
第二单元第一次作业 多线程设计策略 第一次作业的想法是设计三个线程:输入线程,调度器线程以及电梯线程.输入线程获取请求并发送给调度器线程:调度器线程通过查询电梯线程的状态(等待.停靠以及移动),并综合 ...
- 北航oo作业第一单元小结
前言 在经过了三次艰辛的oo作业后,oo课程的第一单元告一段落,这一单元,我作为一个oo小白,开始了解oo的编程思想,也有了自己的一点心得体会.把笔粗成字,不当之处,还请各位大佬多多指教. 一.分析程 ...
- 2019北航oo课程第二单元作业总结..#_#..
学习了之前在写代码是从来没有见过的多线程之后,便迎来了此次电梯作业.说实话,这次作业做得十分的辛苦,虽然在前三次作业中领悟到了java面向对象的精髓,但是再加上了多线程之后,又开始理不清思路,对自己的 ...
- OO作业第一单元总结
一.第一单元作业回顾 系列一作业分为三周进行,都是表达式求导,难度渐进. 第一次实现的是简单幂函数的求导,第二次加入了sin和cos两种三角函数,第三次实现了三角函数内的嵌套以及引入了表达式因 ...
- 大闸蟹的OO第二单元总结
OO的第二单元是讲多线程的协作与控制,三次作业分别为FAFS电梯,ALS电梯和三部需要协作的电梯.三次作业由浅入深,让我们逐渐理解多线程的工作原理和运行状况. 第一次作业: 第一次作业是傻瓜电梯,也就 ...
- OO第二单元单元总结
总述 OO的第二单元主题是电梯调度,与第一单元注重对数据的输入输出的处理.性能的优化不同,第二单元的重心更多的是在线程安全与线程通信上.这此次单元实验之前,我并未对线程有过了解,更谈不上“使用经验”, ...
随机推荐
- 判断ip地址是否为内网ip或局域网ip
bool IsLanIp(string& ip) { ,) == ,) == ,) == "192.") { return true; } else { return fa ...
- 2014-10-24 NOIP欢乐赛
10-24NOIP欢乐赛 ——By 潘智力 题目名称 分火腿 无聊的会议 班服 时间限制 1s 1s 1s 内存限制 64MB 128MB 128MB 输入文件 hdogs.in meeting.in ...
- python进阶12 Redis
python进阶12 Redis 一.概念 #redis是一种nosql(not only sql)数据库,他的数据是保存在内存中,同时redis可以定时把内存数据同步到磁盘,即可以将数据持久化,还提 ...
- 寒假作业第二组P&&Q&&R题解
P的题意是有M份作业,这些作业有不同的截止日期,超过截止日期完成,不同的作业有不同的罚分,求如何完成罚分最低. 首先,从截止日期最长的那个作业到截止日期,这些天数是固定的,所做的就是把这些作业填进这些 ...
- 前端CSS(1)
前端基础CSS(1) 一.css的引入方式 现在的互联网前端分三层: HTML:超文本标记语言.从语义的角度描述页面结构. CSS:层叠样式表.从审美的角度负责页面样式. JS:JavaScrip ...
- myeclipse 最佳设置
http://www.cnblogs.com/wuyifu/p/3593035.html
- GUI的最终选择 Tkinter(八):Message组件、Spinbox组件、PanedWindow组件、Toplevel组件
Message组件 Message(消息)组件是Label组件的变体,用于显示多行文本消息,Message组件能够自动执行,并调整文本的尺寸使其适应给定的尺寸. from tkinter import ...
- (转)linux下控制帐户过期的多种方法
linux下控制帐户过期的方法:原文:http://blog.51cto.com/oldboy/1289144企业里一般给无人管理的角色账户或开发人员临时需求等可以设定账户有效期,提升安全!法一:添加 ...
- python之三级菜单
python之三级菜单 要求: 1. 运行程序输出第一级菜单 2. 选择一级菜单某项,输出二级菜单,同理输出三级菜单 3. 菜单数据保存在文件中 4. 让用户选择是否要退出 5. 有返回上一级菜单的功 ...
- OpenCV图像处理之 Mat 介绍
我记得开始接触OpenCV就是因为一个算法里面需要2维动态数组,那时候看core这部分也算是走马观花吧,随着使用的增多,对Mat这个结构越来越喜爱,也觉得有必要温故而知新,于是这次再看看Mat. Ma ...