OOP 第二章作业总结
实现策略
这里结合一下我画的第三次作业的时序图(可能有画的不好的地方)来叙述一下我的实现逻辑。最开始主线程负责创建必要的线程(输入、调度线程)与请求队列类实例;输入线程负责与人进行交互,将获取到的请求放入请求队列;调度线程则每次从请求队列中取出一个请求,将其分发给三个电梯,若需要拆分,则新建一个次级调度线程,将第二部分的请求的分发任务交给它来实现;电梯线程只需要负责模拟自己电梯的运行即可。
本次作业共享数据的同步互斥主要采用的是 synchronized 方法,再配以 wait notifyAll 等通信方法,完成了线程之间的协作。
程序结构分析
类图分析
第一次作业类图分析
主函数类负责创建电梯线程和输入线程,Request 类负责实现请求队列的功能,Input 类负责将输入的请求放到请求队列里面,电梯线程负责调用 ELevator 类实现的方法来模拟电梯运行。感觉这样设计还挺合理的。
第二次作业类图分析
在上一次设计之上,将 ElevatorRun 类的功能(模拟电梯运行线程)放到 Elevator 类里,新增 Schedule 类充当调度器,即将请求队列中的请求取出并分发给电梯线程,并且由它创建电梯线程,主类只负责创建 Schedule 和 Input 两个线程。其余地方设计与上一次作业一样。
第三次作业类图分析
- 在上一次设计之上,将原有的 Request 类重命名为 ReqQueue 更符合该类所实现的功能。
- 新增 Request 类,这个类是为了复制 PersonRequest 类并向其中增添了 Finish 位,来标志当前请求是否被解决了,这一点在拆分请求中对于第二条请求的阻塞条件判定会用到。(这里用代理的话,就能更简单的实现)
- 新增 SubSchedule 类用于实现拆分请求的二级调度。
在本次作业中,对于请求拆分的处理,我是想实现一个二级调度,当需要拆分的时候,新建一个子调度线程(让他负责拆分后请求的第二部分的分发)。按道理来说,子调度类应该是主调度类的子类,但是我当时认为没有必要再为子调度类复制主调度类的那些成员域,感觉只需要调用主调度类的分发请求的方法就可以了,所以就让主调度类成为了子调度类的一个成员变量,但这样一来又有违于主子这个概念,感觉还是继承要好一点。
代码静态分析
- 由于是 eclipse 用户,所以代码静态分析使用的是 Designite jar 包。
- 三次作业的类复杂度分析
- 三次作业的 LCOM、FANIN、FANOUT 指标都非常相近,LCOM 指标小这点是好的,但是后面两个指标都为 0,表明了我的结构并没有按照分层设计这三次作业,更多的是平级的互相合作,这一点在后续的作业中有待改进。(但总觉得也不至于为 0 吧,有点质疑这个分析包)
- 三次作业的方法复杂度来说,CC 指标较上一次有显著的下降了,第一次平均值是1.3,最大值是4;第二次平均值是2.5,最大值是8;第三次作业平均值是2.7,最大值12。
- 第二次作业 CC 值最大的方法是 Elevator 类的 deal 方法,该方法模拟电梯接送乘客,所以每到一层需要判断是否开门,以及最后一个电梯里的乘客送达之后,判断等待队列里面是否有乘客需要接送,有则将其视为主请求,并接着运行。仔细想了一下,里面确实有一些判断语句能够用一个方法封装起来,这样既提高代码复用性,又能够降低复杂度。
- 第三次作业 CC 值最大的方法便是 Schedule 分发请求的方法,因为这个方法需要判断一个请求,是能给哪一个电梯,或者需要拆分,而这个做出判断的过程,我是使用了一个大的 switch 判断语句,故 CC 复杂度过大,但是感觉这个是不得已的事情。
- 三次作业的 code smell 分析
- Magic Number:三次作业中随着作业的难度的递增,魔数出现的数量逐渐增加,到了第三次已经有了 33 次了,检查了一下代码,主要出现在 Elevator 与 Schedule 类,确实有很多地方可以改为宏定义的方式,下一次需要注意,有助于提高代码可读性。
- Complex Conditional:这个主要出现在第二次和第三次作业中的电梯请求分发的判断地方与电梯开关门逻辑之中。对于后者有改进之处,因为我是把判断有没有人要上来与出去的语句分别放在了开关门方法里面,这一处应该拆分一下职能。
自我程序 bug 分析
- 后两次作业分别被找出一处 bug
- 第二次作业:bug 是在电梯运行模拟处,完全是逻辑错误,在两个地方改变了电梯的 aimFloor 但是 direction 并没有改变,导致电梯上天了。
- 第三次作业:bug 是我的线程在评测机上出现了线程不稳定的情况,反正本地是跑出来没问题,原因是我 的次级调度中心,在获悉前一部分请求结束之后,先告诉主调度器自己结束了再分发的请求,这导致了主调度器在确认所有次级调度器结束之后就直接让电梯下班了,这时若分发工作还未完成则会有提早下班的情况。
发现他人 bug 策略
- 其实互测都是挺佛了,主要就靠随机生成数据,然后靠网上的评测机来验证输出的正确性(由于自己感觉写评测机太麻烦就咕了,逃)
心得体会
- 在设计框架上,现在至少想到有两处可以改进的地方:
- 用工厂方法,实现生成不同种类的电梯:这个主要是由于第二次作业就没有想到以后电梯会不同的情况,所以就把电梯写死了,然后第三次并不想重构电梯。应该可以在电梯类里面定义实现好业务逻辑,然后通过继承的方式,来实现不同类别的电梯,这样才符合开闭原则。
- 在第三次作业的时候,由于我想扩展 PersonRequest 类的功能,实现一个结束标识符,我重写了一个 Request 类,这非常不优雅,其实可以用动态代理实现的,这样就不用傻傻的重抄一遍了(虽然就工作量而言,可能都是差不多的)。关于代理模式,我总结在了另一篇博文中 Java 设计模式 -- 代理模式
OOP 第二章作业总结的更多相关文章
- SQL 第二章 作业
/*第二章 作业*/ create table S ( sno char(2) NOT NULL UNIQUE, sname char(3), city char(2) ); alter table ...
- OOP 第一章作业总结
程序设计结构分析 类图分析 第一次作业 由于第一次作业完成的功能比较简单,而且出于对面向对象设计理念不熟悉(其实现在也不是很熟悉,逃),整个程序设计的非常简单.通过类图(见下)可以看出,程序只有两个类 ...
- OOP第二章博客
OO第二次博客作业 (1)作业分析 三次作业在处理多线程的协同配合时都是使用将同步放在自己写的"线程安全类"(经测试有些许漏洞_,但是不影响结果就是了): 我个人倾向于把wait( ...
- 我永远爱着OOP——第二单元作业总结
第二单元的电梯真是愉♂快呢,多线程编程作为java编程OOP中的重要组成部分,通过这一个单元的学习,我也是有了很多全新的认识 那么下面就先例行一下公事 三次作业分析 第五次作业 设计分析 实现的电梯是 ...
- Java OOP——第二章 继承
1. 继承: ●继承是面向对象的三大特征之一,是JAVA实现代码重用的重要手段之一: ●继承是代码重用的一种方式,将子类共有的属性和行为放到父类中: ●JAVA只支持单继承,即每一个类只有一个父类,继 ...
- java OOP第二章_封装
一. 封装: 属性通过private访问修饰符将其设置为私有的,只有当前类中可以访问,同时提供通过public访问修饰符的公共方法可以给任何类中访问. 通常针对属性提供公共的setter方法进行赋值, ...
- 第二章作业-第3题(markdown格式)-万世想
第3题题目是: 完成小组的"四则运算"项目的需求文档(使用Markdown写文档),尝试同组成员在各自PC上修改同一文档后,如何使用Git命令完成GitHub上的文档的更新,而不产 ...
- oop第二章1知识点汇总
1 方法重写必须满足以下要求: 1 重写方法与被重写的方法必须方法名相同,参数列表相同. 2 重写方法与被重写的方法返回值类型必须相同或是其子类 3 重写方法不能缩小被重写方法的访问权限 2 重载和重 ...
- C++第二章作业
1.(1)if...else 用法 #include <iostream> #include <cstring> using namespace std; int main() ...
随机推荐
- Mysql内置功能《一》流程控制
delimiter // CREATE PROCEDURE proc_if () BEGIN declare i int default 0; if i = 1 THEN SELECT 1; ELSE ...
- 「案例」重新设计 Adobe 的文件类型图标
Adobe 的品牌设计团队负责为公司旗下桌面端.移动端和 web 端的产品进行品牌设计.品牌元素的形式很多,可以是两个字母的产品 logo,应用启动界面,产品里的图标等等. 一个很常见却常被忽视的品牌 ...
- leetcode-118-Pascal's Triangle(生成具有n行的帕斯卡三角形)
题目描述: Given a non-negative integer numRows, generate the first numRows of Pascal's triangle. Example ...
- django 后台格式化数据库查询出的日期
在项目中,我遇到这样的情况,使用ajax获取查询出来的数据,而这些数据中某个字段是日期datetime格式,在模板中显示的样式很怪异.由于前端使用了js控件,也不能使用django的模板过滤器. 所以 ...
- node.js调试方法
第一种方式:node内置的调试器 在程序中添加debugger,然后在启动node程序时,使用debug模式启动 1.node debug my_event.js 2.使用node文档中各种命令,进行 ...
- vue 数据(data)赋值问题
总结一下我遇到的一个纠结很久的问题. 在项目中需要用到后台的数据对前端渲染,使用到了vue整合的axios,使用vue中的钩子函数在页面组件挂载完成之后向后台发送一个get请求然后将返回后的数据赋值d ...
- 【算法笔记】B1040 有几个PAT
1040 有几个PAT (25 分) 字符串 APPAPT 中包含了两个单词 PAT,其中第一个 PAT 是第 2 位(P),第 4 位(A),第 6 位(T):第二个 PAT 是第 3 位(P),第 ...
- Hystrix - 踩坑回忆
1.Unable to connect to Command Metric Stream 异常 Finchley版本使用Hystrix存在此问题.网上常规解决思路: @Bean public Serv ...
- Python爬虫常用之登录(一) 思想
爬虫主要目的是获取数据,常见的数据可以直接访问网页或者抓包获取,然后再解析即可. 一些较为隐私的数据则不会让游客身份的访问者随便看到,这个时候便需要登录获取. 一般获取数据需要的是登录后的cookie ...
- linux下 git使用小记下
之前都是自己windows做脚本,完成文件的备份和管理,第一次使用git 转发一个博主很有用的笔记 ,并在此做了一点补充学习了 引用:https://www.cnblogs.com/chenfuli ...