OO——UML解析
第四单元博客作业
一、前两次作业架构设计
1. 第一次作业
1.1 结构分析
对于第一次的作业,完成的任务是类图的解析。由于官方的jar包已经帮我们完成了大部分的工作,所以我们需要做的任务就是对解析完成后的标签进行处理,完成相应的函数的功能。
通过阅读作业指导书,我发现本次作业中的函数都是查询类的,因此我选择通过构造大量的容器来降低查询的复杂度。通过对UML图的标签进行分析,我发现属性、操作、类这些元素以树的形式组织在一起,因此我就朝着这个方向来进行了容器的建立。对UML图标签的解析我发布在了作业的讨论区中。链接
1.2 复杂度分析
从图中我们可以很容易的看到,这个类中的构造函数的复杂度非常的高,这主要是因为我将容器的构造工作都放在了构造函数中进行,导致了在构造函数中出现了遍历和大量的对容器的操作。
dealExp是一个异常处理函数,是在某些函数初始的时候调用,用来判断ClassNotFoundException和ClassDuplicatedException这两个异常的,我在判断的时候采用的方式是遍历整个容器,看是不是存在这个Class或者这个Class出现了几次,因此导致了for循环中的if分支的嵌套。由于这个函数的高复杂度,调用这个函数的函数的复杂度也随之提升了上去。
此外,对于一些需要bfs来逐层查找父类的函数,其复杂度也会高。
2. 第二次作业
2.1 结构分析
在第二次作业中,我采用的思路和第一次一样,对于新添的对于状态图和顺序图的查询,我也是先分析标签,找到其中的树形结构,然后通过构造容器来解决查找时候的高复杂度问题。但是,由于代码的增多,一个类完全不能够再规范的代码格式中完成所有的函数,因此我在这里通过类继承的方式来对每个类的功能进行分区。
MyUMLGeneralInteraction是保留了对类图的查询,即第一次作业中的功能。Son类是对顺序图和状态图的查询。Check类是对三个规则的检查。
2.2 复杂度分析
对于Son类中的getSubsequentStateCount方法,我在其中使用了BFS来实现对所有可达结点的遍历,因此导致了复杂度的提高。而对于Check中的方法,也是如此的原因。因为再Check中需要检查循环继承和重复继承,这些函数都是需要逐级向上的检索每个类的父类或者接口的父接口以及它们直接的实现关系,我才用了BFS的方法,因此导致了复杂度的提高。
二、架构设计以及对OO方法理解的演进
1. 表达式求导
整体来看,在第一次的作业中,由于面向对象的学习不够深入,还不知道继承和多态的思想,程序总体上还是偏向面向过程,只不过用类来代替了原来C语言中的函数而已。但这间接导致了本次作业中程序的内聚性较好。虽然最终程序的正确性没有什么问题,但是过多的if分支导致了在编写过程中调试的复杂程度程指数性提高,浪费了大量的时间。并且我没有建立将读入多项式并判断合法性的类,这不仅增大了main函数的负担,也降低了程序的鲁棒性。一旦输入要求发生更改,就要对程序进行大幅度的调整,甚至重构。
总结第二次作业,通过对面向对象思想的学习,我对类的使用有了进一步的认识。通过将不同功能的处理交给不同的类,以增加类的方式来减少每一个类的规模。这样的分工,可以使得每一个类的功能明确,符合高内聚低耦合的原则。但是,从前面的分析中可以知道,此次作业并不是所有的类的设计都合理,部分类的分工不是那么的明确,倒更像是面向过程调用函数的思想。此外,本次作业中没有体现出继承和接口的思想,还不是一个很好的面向对象的程序。
在第三次作业中,我采用了继承的思想。将Function和Rule分别设计了两个继承树,虽然每一个树只有一层,但相比前两此作业的程序,类之间的耦合性大大降低,每个继承树的功能交集大大降低。这样在debug的过程中,当定位到bug的所在类,就可以放心在该类或者该类的继承树中寻找bug,降低了debug的难度。
2. 多线程电梯
第一次作业是电梯作业的第一次,也是我多线程变成的第一次实践。任务是编写一个多线程实时电梯系统,采用FAFS的调度方式。由于第一次作业中没有涉及到多部电梯以及捎带的情况,因此来说是比较简单的。我采用的是指导书提示部分中的模式,即生产者消费者模式
- 主线程进行输入的管理,使用ElevatorInput,负责接收请求并存入队列
- 开一个线程,用于模拟电梯的活动,负责从队列中取出请求并进行执行,执行完后继续取继续执行
- 构建一个队列,用于管理请求,且需要保证队列是线程安全的
第二次作业中,电梯仍然是一部电梯,只是算法上用ALS捎带算法代替了FAFS的傻瓜调度,所以整体的架构我还是沿用第一次作业,仍然以生产者消费者模式为主。
在第三次作业中,要求三部电梯进行调度,并且不同的电梯的可达楼层不一样,这不仅导致了多线程的线程安全问题,也导致了请求的分配问题。因为一个人的请求有可能需要通过换乘来处理。我的程序主要保证的是正确性,所以牺牲了很大一部分的性能。我解决这种情况的方法是,将这种请求分割为两个请求,以1层为界,因为所有的电梯都可以到达1层。
并且在整体的结构上,我这次采用的是课件上的Worker-Thread模式,相当于对之前的作业进行了重构。
- Main类是主类
- Request类是请求类,是对提供的PersonRequest类型的请求的一个封装,因为考虑到要对请求的起始楼层进行修改
- ClientThread类是输入类,负责将接收到的请求存储到Channel的请求队列中
- Channel类是整体的调度器,用来将请求跟据不同的情况分配到不同的电梯中,并且在这次作业中,我采用请求队列和调度器二合为一的方式
- WorkerThread类相当于前两次作业当中的电梯类
3. 地铁线路查询
对于第一次作业,整体上的层次比较简单。Main
作为主类,通过AppRunner.newInstance()
调用MyPath
和MyPathContainer
两个类。
从图中我们可以看到,在第二次作业中,我没有进行继承,MyPath
和MyGraph
两个类中的很多部分都是直接从上一次作业中复制过来。当时在做作业的时候想到这一次的代码不至于超过标准行数,所以说也就没有采用继承了
在第三次作业中,我进行了进一步的重构。首先由于这一次作业又增加了几个方法,所以一个类显然已经不能够在合法的范围内完成这么多的功能,所以这一次我的MyRailwaySystem
类继承了MyGraph
类,在新的MyRailwaySystem
我只是实现了本次作业新增的方法。
4. UML图的解析
这一部分在上面已经写的比较详细,在这里就不赘述。
三、对测试的理解和实战的改进
1. 对测试的理解
虽然在这么多次作业中,我体会过强测加互测没BUG的喜悦,也经历过强测爆炸的绝望,有时候我会恨测试,觉得构造这么有针对性的样例有什么必要?就为了扣我那几点分?但几次之后,我就改变了对测试的看法。从老师每节课对测试重要性的强调,以及举的因为BUG而机毁人亡的案例,都让我深刻的去重新审视自己对测试的看法。
测试,实际上是程序开发过程中最重要的一环,第一次听老师说测试人员的工资比敲代码的人员要高的时候,我十分的不解,不就是提供几个样例吗,有不需要费什么功夫,为什么赚的多?但是通过我在互测过程以及自己编写代码的时候的测试,我才发现要想构造出一个有效的BUG,是多么的困难。有几次的作业,我自己都觉得没有问题的时候,放到别人那里,就会秒爆BUG,这就说明了我对测试的疏忽。
2. 测试实战
最初的时候,我采用的是手动构造样例,因为一开始的程序,我们是很容易的从结果中用肉眼来判断正确性,但是,也就有效了大概两次作业吧,后面的作业的输出就变得不是人看的了,你甚至不知道自己的输出是对还是错。在这种情况下,我接触到了对拍和单元测试这两个伟大的东西。
对拍,顾名思义,就是将两个人的输出结果进行比对,通过几次的对拍下来,我发现其实三个人就基本上能够达到很高的纠错率,毕竟大家的水平都是很高的,在这个地方的“异类”,大概率就是BUG了。
而对于单元测试这个东西,在之前我是从来没有接触过的。但是,我经过了几次的尝试,很快的就体会到了它的高效性。虽然也是自己构造样例,但是单元测试能够对一个函数,或者是一个类这样的单元来进行特殊的一对一的测试,这样的话,我们就可以很容易从结果中知道这个单元的正确性。这个方法在地铁线路的查询以及UML图的解析这两大部分的作业起到了很大的作用。
四、课程收获
1. 面向对象的思想
面向对象作为一种思想,在我接触计算机专业之前就已经不下数次的接触到,无论是java、c++、python,甚至是c,选用什么语言作为面向对象的载体并不影响对这个思想的学习。在本学期的课程结束后,我对面向对象有了一个很深的理解,通过作业的实战练习,慢慢的将原来的过程式编程改编成现在的对象式编程,并且通过重构等方式对程序的结构有了一个更细致的掌握。
2. 测试的重要性
正如前面所说的那样,测试的重要性我是在OO这门课程中才接触到的,虽然我们现在编写的程序不会造成机毁人亡的大型事故,但是从几次的强测爆炸就已经让我受不大了了。
3. 多线程的编程
在以前,我从来没有接触过多线程的编程,在本学期操作系统和OO两门课程的共同知道下,我接触到了这个未知的领域。多线程虽然在文字层面是很好理解的一个东西,但是实际编写起程序来,真的是让人头秃。无论是死锁,竞争还是异常,种种的错误让我在第二单元的作业中受尽了苦头。
4. Checkstyle
当然,我学到的不是Checkstyle的编写,我采用这个词作为题目,是因为它在不知不觉中,真的改变了我很多。从一开始生硬的在编写完程序后对着Checkstyle的报错一点一点的修改,到后来在代码编写的过程中就对Checkstyle进行了注意,我真的收获很大。对于以后走上工作岗位,每个部门也有每个部门的规范,现在接触到这种编程的规范,真的是收获很大。
五、对OO课程的建议
1. 对于查重程序的更新
在这一学期的OO作业中,我遇到的一个很不快的事情就是在第五次作业的时候被怀疑是和一个素不相识的同学的代码重复度高于50%...在接到检查的结果后,我们两个都十分不解为什么会有这样的判定。通过看检查报告,可以很明显的看出我们两个被高亮的代码不是抄袭导致,完全是两种代码,但是却被认为是重复,这也很无语。虽然最后给我们了一个清白,但是这依旧让人的心情很不好。
2. 互测部分的分值继续降低
虽然今年的OO互测相比往年来说已经有了一个很大的进步,没有什么南湖见,刷知乎的现象,但是我认为如果能够再减少一下OO互测的分值,会对这门课程起到一个更加积极的推动作用。
3. 实验课
真心希望,第一单元的实验课,不要上来就让人调试java代码,这对于一个刚刚自学了怎么用java语言写c程序的孩子来说太难了...