OO作业第二次总结
多线程电梯
设计策略
这是一个典型的生产者消费者模型,但由于是第一次进行多线程的编写,不知道如何下手。但至少我知道了首先要构造生产者、托盘、调度器以及消费者。作为资源共享区的托盘一开始十分让我头疼,我们要保证我们的托盘是线程安全的,因为我们既要往里放东西,又要从里面取东西,什么wait
,什么notifyall
,总之很烦。好在接触了优秀的阻塞队列,就不需要自己造轮子了!
Main
:主线程,相当于生产者,在主线成立生产数据,并加入到阻塞队列中;Scheduler
:调度器线程,用来不间断扫描托盘,将可响应的请求分配给最合适的电梯;Req
:请求类,用来封装一个请求,相当于c
语言中的结构体,同时有很多个接口可以用来访问相应的属性参数;Elevator
:电梯线程类,记录了电梯的属性:编号,状态,运动量等等,run
方法内是模拟电梯运行,每隔\(3s\)更新一次电梯的状态和运动量;InputHandler
:用来处理输入,解析出入的字符串;
度量分析
COB
:对象之间的耦合性;DIT
:继承树的深度;LCOM
:Lack of cohesion method
(不知道怎么翻译,缺乏凝聚的方法?)NOC
:子类的数目;RFC
:Response for class
;WFC
:weight method complexity
(方法复杂度);
其中我的电梯类的方法复杂度是最高的,电梯类中的方法有:
run
:模拟电梯运行,更新电梯状态;judgeIncidentForMain
:判断是不是主请求可稍待的请求;getReq
:获取请求;turnOnE
:打开电梯内请求的某一层的按钮的灯;turnOnF
:打开楼层间按钮的的上行或者下行按钮的灯;whetherOnE
:判断该层楼的电梯内按钮是否是亮着的;whetherOnF
:判断该层楼的楼梯间按钮的上行或者下行按钮是否亮着;getMetric
:获取该电梯的运动量;canIncident
:判断该请求是否是可以被捎带的,专门为FR
请求设计;isWFS
:判断电梯是否是处于等待服务状态;
方法十分之多,但都是是很依赖于电梯的方法。另外Scheduler
类的复杂度也稍高是显而易见的。
高内聚低耦合
内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。
耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。
我个人认为,我的电梯模块和调度器模块的内聚做的比较一般,但是问题不大,主要是因为这些方法十分依赖这个对象,几个主要类之间的对象耦合性太高了,这样不好。
自己程序的BUG
- 没有处理一楼
DOWN
和二十楼UP
的非法请求;
类图
偷个懒,只对最后作业七的类图分析。
文件管理系统
设计策略
由于我们每次都要不断扫描我们的监控范围,因此我们应该为每一个监控对象设置线程。我们要不断改变获取和设置文件对象,因此文件类需要被改造成线程安全类。本次作业的逻辑比较简单。
Main
:主线程,输入的入口在这里,同时启动其他线程;InputHandler
:用来处理输入;Monitor
:监控线程,扫描监控范围,触发器在这里响应;SafeFile
:线程安全的文件类;Req
:请求类,用来封装请求,类似c
语言中的结构体;Detail
:记录细节信息线程,每隔一段时间写一次detail
文件;Summary
:记录触发器触发次数;TestThread
:测试线程;
度量分析
对象之间的耦合性最高的是Monitor
类是不出意料的,它既要从主线程那里获得请求,又要将数据传递给Summary
和Detail
类来做记录工作,降低耦合性是接下来要做的工作。方法复杂度最高的是Monitor
类也是情理之中的。
自己程序的BUG
- 监控范围一样的对象但是触发器或者任务不一样的都算作不一样的,关于这个
bug
指导书我是理解正确了的,但是写的时候还提醒了一下自己,结果最后还是忘记了,主要怪自己没有认真自测,很难受;
类图
多线程出租车
设计策略
本次作业的设计思路十分简单,Main
作为生产者输入请求,并产生一百辆出租车先线程,启动每一辆出租车,启动调度器线程。
在调度器中需要完成的就是为每个请求分配抢到该条请求的出租车,然后在这些出租车中选出最符合要求的那一辆,并将该条请求传递给该辆租出车,接着出租车就根据这条请求进行驾驶。
Main
:主线程,具有生产者的功能,输入的请求在这里解析,然后给到阻塞队列reqList
中去,同时在主线程中,生成一百辆出租车,并将其保存在taxiList
中;Req
:请求类,将一个请求封装,在这个请求类中,我们可以获取一个抢到单的出租车的竞争队列,并在三秒窗口期结束之后,选择最优的出租车;Taxi
:出租车线程类,在这个类中,也是将出租车进行了封装,我们可以在wait
状态时,随机选择租出车行驶的方向,在接到单之后,选择任意一条最短路径去行驶,在接到客之后,再任意选择一条最短路径行驶到目的地;Scheduler
:调度器类,在这个线程类中,我们将不断的扫描我们的请求队列和出租车队列,每一个请求扫描一遍出租车,将可抢单的出租这加入到该条请求的竞争队列中,当某条请求窗口时间结束,那么将该条请求派单给选出的最优出租车;Map
:地图类,用来将输入的地图文件初始化,存入到一个二维数组中,并且判断输入的文件是否是正确的’gui
:工具集合,就不做过多的介绍了;
度量分析
Taxi
类与其他类之间的耦合性太高了,应该降低;
自己程序的BUG
- 出发地和目的地的位置相同的请求没有处理;
- 没有完美达到\(200ms\)时间间隔。。。。。。
类图
在这个类图中很清晰的展示出了数据间的共享传递情况,Map
类中接受Main
中打开的map
文件,并将其做好处理之后存储下来;Scheduler
类中获得在Main
的输入的请求队列reqList
和产生的出租车队列taxiList
,每一个Req
类先获得从Main
中得到的数据,又在调度过程中获得可以抢到该条请求的Taxi
,并加入到自己的出租车竞争队列中去。
怎么找别人的BUG
- 虽然输入的合法性基本在公测中已经测试过了,但还是可能存在漏网之鱼,我们可以通过其他刁钻的角度找到错误;
- 思考可能的
crash
情况; - 做一些基本的功能性测试;
- 阅读代码从两个角度寻找是否可能出现错误,其一是线程安全,其二是设计逻辑是否正确。
心得体会
随着写过的oo
作业的增多,代码量的增加,我对面向对象的程序设计和多线程程序的编写的熟练度得到了提升。因此感觉这几次的作业不是很费力。
最重要的是要早做准备,我们应该在写之前设计这次作业的思路和逻辑。我总是在写之前,和小伙伴们先讨论好整个作业的设计框架,每一个环节的逻辑应该是怎样的,为后面程序的编写带来了很大的方便。