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 启动提示:错误2系统找不到指定文件
详情见这个方法 其实就是更改了启动目录导致的 https://blog.csdn.net/su749520/article/details/78963878
- 多线程DP
Matrix Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Subm ...
- 如何 3D 打印一个密码锁
简评:这篇文章介绍怎么用 3D 打印机做一个密码锁,巧妙地利用机械结构的变化实现锁的功能,相当有趣! 3D 打印机非常适合打印静态物体.如果你够聪明,还可以打印出功能物件.如果你特别特别聪明,那你能做 ...
- Kettle 值映射
在费用转换里面做了两个值映射.一个是编码.一个是名称.其中两个值映射设置不一样效果不一样. 第一个编码映射 目标字段名不为空,则表示会新增字段.其中复核源值条件的都会转换为目标值,不符合条件的会用[不 ...
- #PHP 数组添加元素、统计数组相同元素个数、改变数组key值~_~
一.数组添加元素 1.定义和用法: array_push() 函数向第一个参数的数组尾部添加一个或多个元素(入栈),然后返回新数组的长度. 2.语法: array_push(array,value1, ...
- python全栈开发_day7_字符编码,以及文件的基本读取
一:字符编码 1)什么是字符编码 将人能识别的字符等高级标识符与计算机所能识别的二进制01进行转化,这之间的交流需要一个媒介,进行两种标识符之间的转化. 字节的存储方式为八个二进制位 2)乱码 存放数 ...
- mybatis的CRUD实例(四)
接下来我们来实现新增用户功能: 一.新增用户 这里我们使用的sql为:insert into user(username,birthday,sex,address) values ("lwj ...
- 基础篇:6.5)形位公差-基本规则 Basic Rules
本章目的:述说形位公差的基本规则 1.代表规则的修正符号与使用情况: 使用情况举例: 2 有关术语 为了明确线性尺寸公差与形位公差之间关系,对尺寸术语将作进一步论述与定义. //无需强记,但希望现有 ...
- FileZilla上傳報錯:421 There are too many connections from your internet address
起因:2019年01月27日晚九點左右,想要將一個50MB+的文件夾上傳到阿里雲的虛擬主機上,使用FTP 工具FileZilla,出現了上傳一段時間後提示421 There are too many ...
- centos7 装机配置及 mysql 安装过程
打开网卡,使操作系统可以上网 1 ip add 查看网卡,lo是回环网卡可以忽略,ens33为实际网卡. [root@localhost ~]# ip add 1: lo: <LOOPBACK, ...