OO第四单元总结
OO第四单元总结
一、作业架构设计
第一次作业
第一次作业主要还是基于对UML图的理解,完成对UML的解析,需要实现的功能如下:
模型中一共有多少个类
类中的操作有多少个
类中的属性有多少个
类有几个关联
类的关联的对端是哪些类
类的操作可见性
类的属性可见性
类的顶级父类
类实现的全部接口
类是否违背信息隐藏原则
在初步构建时,我主要在一开始对UML的各种类型进行了分类。对于分好类后的元素,class的个数,operation的个数,attribute的个数,都能直接进行统计。而对于类之间关联关系的建立,则是遍历association,找出association从而找出所对应的类完成的。在存储的过程中,使用HashMap也能非常方便的完成。
对于顶级父类,我采用的dfs,首先我们可以得到对类直接的继承关系,通过逐级遍历,最终能得到顶层父类。对于类实现的全部接口的得到,也可以采用类似的方法。
总体架构如下图所示:
第二次作业
对于类图的解析,在第一次作业的基础上几乎没有变动。而顺序图和状态图的构建也相对简单。
最开始,我先把元素按照三类图进行分开,经过观察,除UML_Attribute之外,其余都为各类型图所特有,因此,可直接分开,而UML_Attribute和UML_Interaction的parent一样则可以判断是属于顺序图,否则则属于类图。这样,我们的第一步工作就完成了。
然后,只需要把对应的元素传给对应的图去处理。
我觉得这次作业的难点在于PreCheck,同名成员的检查只需要根据association找出对端的相连的class,相对比较容易。循环继承我则采用dfs的方法,记录下路径,如果再次回到路径上的点,则代表有环。对于R003(任何一个类或接口不能重复继承另外一个接口),我则采用找临接点去求他们topParent的方式,如果临接点的topParent存在交集或者临接点本身不满足R003,说明不满足。
总体框架如图:
本次作业存在的问题
在第一次中,没考虑到多继承,从而导致强测挂了几个点。
在第二次中,虽然是基于第一次的代码,但感觉在增加Precheck时,代码的复用性不够好。
二、四个单元中架构设计及OO方法理解的精进
2.1 第一单元作业
第一单元为表达式的求导。第一次只有一个多项式的求导,非常简单,但格式错误坑了不少人,尤其是空白符。第二次加入了三角函数项,但三角函数的求导其实通过自己实现手算发现形势是可以统一起来的。虽然看起来需要加入 功能不多,但当时还是重构了许久。第三次作业难度很大,我采用表达式树的方法,按照括号和运算符,对表达式一步步进行拆分,构建成表达式树,并递归的进行求导,但是由于化简过于复杂,我则没有进行化简,这一算是一个遗憾吧。
在此次作业中,由于对面向对象的理解不够深刻,代码几乎还停留在面向过程的阶段,每次作业也差不多只能重构,所以完成的比较艰辛。
2.2 第二单元作业
该单元是带给我困扰最多的一个单元,我们从单线程到了多线程程序的设计。第一次作业为傻瓜电梯,规则就简单的FIFO,因此就直接一个队列就能搞定。第二次作业难度激增,不再采用FIFO,而使用最短调度时间。因此我用指导书采用的主捎带算法,LOOK算法,贪心算法都进行了一定的尝试,虽然最终因为对多线程的理解不到位,未能正确使用wait和notify造成了死锁,导致中测挂了一个点,但还是学到了不少。最后一次做作业,则是多电梯的调度。我将一楼设置为中转楼层,一旦不能到达,按照前去一楼进行中转进行调度。不同电梯 之间的协作,我采用的是预先分配法,一旦可以直达,则直接分配给相应电梯,否则在能搭载的电梯上选择已经获得请求数目最少的电梯进行分配。对每一个电梯内部的运行,则完全使用了第二次作业的代码,采用了主请求捎带法。这一次总算把第二次作业中不能正确理解死锁弥补了过来,对多线程使用的还算顺手。
在这个单元的学习中,我对多线程模式有了更好的理解,更深入了理解了线程之间的同步,死锁,生产者与消费者问题等等。在设计上,我也更加注重模块的可拓展性,努力去降低各个类之间的耦合,更加清楚的对各个类的功能进行划分。总的说来是在挣扎中进步。
2.3 第三单元作业
本单元为JML规格,即通过给出的JML写相应的代码。使用JML的话,我们就能够描述一个方法的预期的功能而不管他如何实现,让我们最开始能尽量从面向对象的角度进行编程,最先去关注整体的设计,再去关注具体方法的实现。同时,对程序的描述从自然语言变成了逻辑严密的规格,对postCondition, requirements等等都做了一系列的要求,逻辑性和无二义性得到了进一步提升。第一次作业没有什么难度,只是要注意对于时间复杂度比较高的部分可以分到不同的地方进行。第二次作业则加入了判断连通性和最短路径,我则采用了Floyed算法进行求解。第三次作业中,我使用的dijsktra,我们可以观察到,无论是求最低票价,最少换乘,最高满意度,路径的计算,我们都可以用相同的方法,只是权值不同。因此我设计了一个calculator类,用于路径权值的计算。
这一单元最大的收获在于对JML规格的理解,通过JML,我们从更高的角度看待了问题,了解了在一个大的模块和工程之中,应该怎样去设计和架构。并且在这个作业中,对于模块的封装性,也进一步得到了加强,我们通过接口的实现对关键代码进行了实现,我对面向对象的理解也更加深刻起来。
2.4 第四单元作业
最后一单元是对UML图的解析。该单元是oo的收官之战,虽然在烤漆的时候布置这个作业让我很不爽,但这次作业让我对UML类图,状态图,顺序图,都有了比较深刻的理解。主体框架课程组已经给我们搭建好,我们要做的就是把定义给我们的接口进行实现。一开始我很懵逼,但同学们在讨论区的热情贡献帮助了我不少。最终顺利的理清楚了各种UMLElement之间的层次关系,完成了这次作业。
这一单元中,模块之间耦合变得更少,也更加面向对象了,我们只需要创建对象,相应的事情交给相应的对象去完成即可,各个类之前的保护性和层次之间的清晰性也变得更好。
2.5 总结
从面向过程到面向对象的转变,从单线程到多线程的转变,我深入理解了封装、继承、多态,抽象,接口,代码复用和工厂模式;也掌握了线程之间的协作,使用多线程的方法使程序更高效的运行。同时,我对整体结构该如何架构也有了更深刻的理解。
三、四个单元中测试理解与实践的演进
在一二单元作业中,我们对于模块的测试主要靠自己手动构造边界条件和构造评测机进行。
手动构造边界条件即肉眼debug法,根据你奇奇怪怪的脑洞想到一些测试的边界条件,例如空格还包含'\t'等等。
构造评测机即自动生成满足指导书给定要求的测试数据,然后构造对结果的定量限制(如第二单元作业)或者直接进行对拍即可。这个对于互测环节是非常有效的,可谓是写一个说不能能用三周系列。例如下面就是一个简单的生成电梯测试数据的程序,当然还可以通过其他一些限制针对性的进行测试:
import random
t1=0
t2=0
for i in range(40):
t1=random.randint(0,40)
t2=random.randint(0,9)
if(random.random()<0.2):
t1+=1
if(random.random()<0.3):
t2=(t2+2)%10
s1=random.randint(-3,20)
while(s1==0):
s1=random.randint(-3,20)
s2=random.randint(-3,20)
while(s1==s2 or s2==0):
s2=random.randint(-3,20)
print("["+str(t1)+"."+str(t2)+"]"+str(i+1)+"-FROM-"+str(s1)+
"-TO-"+str(s2))
这两种方法相信经过了惊险oo互测环节的各位都非常熟悉了。
第三单元作业则带给了我们一些全新的方法进行程序的测试,对于每种方法,我们可以通过JUnIt4进行测试,这种测试给我们带来了很大的便捷性。同时,我们还可以查看测试的覆盖度,这样才不会遗漏测试。下面是我使用的部分样例:
package main;
import graphelem.MyPath;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class MyRailwaySystemTest {
private MyRailwaySystem myRailwaySystem = new MyRailwaySystem();
@Before
public void setUp() throws Exception {
int[] m = {2, 3, 4};
int[] m1 = {1, 2, 3, 5};
int[] m2 = {4, 10};
int[] m3 = {14, 15};
int[] m4 = {29, 37, -11, 42, -11};
int[] m5 = {-41, -46, 54, 59, 22};
myRailwaySystem.addPath(new MyPath(m));
myRailwaySystem.addPath(new MyPath(m1));
myRailwaySystem.addPath(new MyPath(m2));
myRailwaySystem.getLeastTransferCount(1, 5);
myRailwaySystem.addPath(new MyPath(m3));
}
@Test
public void getLeastTicketPrice() throws Exception {
// int result = myRailwaySystem.getLeastTicketPrice(1, 4);
// Assert.assertEquals(result, 3);
int re = myRailwaySystem.getLeastTransferCount(42, -11);
Assert.assertEquals(re, 0);
Assert.assertEquals(myRailwaySystem.getConnectedBlockCount(), 3);
myRailwaySystem.getLeastTicketPrice(1, 15);
myRailwaySystem.removePathById(3);
Assert.assertEquals(myRailwaySystem.getConnectedBlockCount(), 2);
int[] m4 = {14, 15};
myRailwaySystem.addPath(new MyPath(m4));
myRailwaySystem.getLeastTicketPrice(1, 3);
}
@Test
public void getLeastTransferCount() throws Exception {
int result = myRailwaySystem.getLeastTransferCount(1, 11);
Assert.assertEquals(result, 2);
}
@Test
public void getLeastUnpleasantValue() throws Exception {
int result = myRailwaySystem.getLeastUnpleasantValue(10, 1);
Assert.assertEquals(result,656);
//System.out.println(result);
}
@Test
public void getEdgeSat() {
int result = myRailwaySystem.getEdgeSat(2, 3);
System.out.println(result);
}
}
而针对JML与代码实现的相符性,我们有JMLUnit测试,根据JML自动生成测试数据,虽然貌似课程组给的JML在这里用不了,但是经过自己的 时间,发现该方法对各种边界条件都有很好的覆盖。
第四单元由于烤漆的原因,只是进行了简单的测试,并未深入探究其他方法。
四、课程收获
总的来说,oo课程给我的收获超级多,这大概是这学期每周占用我时间最久的一门课了吧(我的周末几乎都献给了它)
作为一只大二上才开始水水的写C语言和python,毫无java基础的小白,到基本上完成大部分要求,整个过程是艰辛的,但又充满成就感的。当你看到第一次用高级语言写这么长的代码,当你看到你写的多项式解析的Parser能正确运行,当你看到改完一个个bug之后多项式能正确输出结果,当你看到电梯里消失的那个人正确的回来了,是一种怎样的欣喜,就像感觉是无数熬过的夜都是值得的。
在java的学习过程中,我也明显感觉到自己写代码能力的不足。大概是无法像其他dalao一样非常轻松的就构建多种方法,甚至还去分析各种方法的性能,也不能像他们一样也出一手漂亮的代码,我就挣扎在每次作业的完成线之上,努力去提升性能。面对各路大神,太容易被自己菜到了,比如自己写电梯调度的时候,因为自己写电梯写的太烂,以至于我自己做电梯都产生了深深的恐惧......
当然,在学习面向对象的过程中,收获还是超级多的。
- 熟练掌握了java语言
- 掌握了接口,继承,多态
- 形成了一种从对象功能角度考虑的思维模式,能尝试对程序进行模块化处理,是复用性得到提升。
- 对测试集的构造,进一步得到提升,同时还外加掌握了怎么使用命令行和批处理运行java程序,怎么使用matlab和python自动测试代码
- 掌握了多线程,对消费者和生产者模式有了更深的理解
- 知道怎样更好的去选择对象
- 了解了JVM和UML,对契约式设计和整体架构有了更好的理解
五、给课程组的建议
在给建议之前,首先感想一下今年课程组对oo的改进,给我们搭建了一个这么好的互测平台,互测屋的建立比往年的一对一模式有了很大的提升。
我的建议如下:
- 提升中测分数占比,或者增加中测测试点,通过率达到百分之九十以上即可进入强测。有时候看到只有一个隐藏点不过又找不到原因真的很心塞(毕竟强测占比超级大)
- 增加课堂上的范例讲解,并给出代码示例。oo主要还是在练习中理解,比如继承和接口,知道了原理但没有具体的使用实例还是有一点慌,在平时也不会刻意去练习,而是照着固有的思维模式去做。
- 部分project难度衔接有点不太合理,比如第一次过于轻松,第二次和第三次难度跳跃过大,可以适当增加第一次难度,或者在第一次作业的基础上再加上一些对后续相关要求的代码的阅读。例如对电梯的第一次作业,就可以同时下发一些有关多线程的设计的样例,让同学们自行学习,方便第二次的架构。
- 要是能像计组一样在cscore平台推出教程,就更好了。