喜讯:一学期的
总结本单元两次作业的设计
第一次作业非常可惜,因为时间原因没有交上,导致了一次无效作业。
既然是最后一个单元,本着对自己这个学期学习的成果检验,我在构思整个代码的架构上花了大量的实践,思考如何解构,如何避免后面添加需求的时候重构,如何更加符合面向对象的思想。最后采用了如下的方式:
此为第二次作业导出的UML图,继承于第一次作业:
这几次作业说不上用了什么设计模式,只是遵循我所理解的专人专事原则,对类图、时序图和状态图分别创建了一个XXXMap作为输入与实际的实例交互的中转站,中转站中可能只是几条语句表明一个处理的顺序,然后具体的事情让具体的对象内部自行处理,存储也是具体的在每个对象之中进行,如MyMachineState,MyInteraction,MyClass等等。
从上面的UML的水平结构可以看出是一个非常具有逻辑性的横向和纵向的结构,比如UmlMap和UmlSequenceMap和UmlInteractionMap在同一水平级别上,MyClass,MyInteraction和MyStateMachine在同一个水平级别上,然后对MyClass中的MyOperation进行了细分,因为涉及到很多对类和接口中方法的查询,总体而言理顺了结构之后写起来也特别得心应手,不会因为全部的方法和属性堆在一起而变得混乱。
具体而言,分析一下自己的代码:
- 由于输入数据的特殊性,在建模的初始化过程中需要一个内在的顺序,所以要注意先将各种输入语句按类别保存,然后再进行统一的建模,而不是一边输入一边建模,这样会导致大量的空指针异常找不到自己的_parent。
- 面向需求分析,什么是可以省略不需要花费时间的:
- 相同的类名或者属性名的时候,所以我对类的名字和id和内容都采用了渐进的访问模式:
- Map<String,String> name2Id
- Map<String,MyClass> Id2Class
- Map<String,List>name2DupClass
- 这样做的目的是当查询到 DupClass 中 containsKey(name) 的时候直接返回类名重复异常,否则当查询到name2id.containsKey(name) 的时候通过id进而对齐继续访问:id2Class.get(name2id.get(name))。
- 相同的类名或者属性名的时候,所以我对类的名字和id和内容都采用了渐进的访问模式:
- 建立节点表,分析时空复杂度,选择算法:
- 在地铁线图的单元的最后一次作业中,因为拆点加上Floyd导致时间复杂度超出预算,得到了有史以来最低分数,所以后面的作业都会谨慎思考能否使用Floyd还是需要广搜深搜,还是需要dij,还是需要spfa。在这次的作业中首先没有拆点的出现,其次最多400条建模语句,进而最多400个节点,而且若节点之间有联系的话,平均两个节点需要2.5条建模语句,所以大约只有160个节点,做一次Floyd完全不会超出时间要求,所以就大胆建图,floyd解决回路和重复继承实现的问题。
- 用包装的方式拓展基础类别的功能和属性,这个在设计模式中属于代理模式。由MyClass代理UmlClass,由MyInteraction代理UmlInteraction等等,这样扩展了自身的属性和方法,如可以设置自身拥有的Operations和Attributes还有parent,在初始化的时候还可以指向自身的父亲,通过
sumAssociationCount = selfAssociationCount + parent.sumAssociationCount
获得父亲的关联等等。
总结自己在四个单元中架构设计以及OO方法理解的演进
感觉一个学期下来自己的代码也有面向对象的味道在里面了,虽然说还是会有的时候出现类超过500行的情况,但是不会让一个main中处理所有的事情。还记得刚刚开始OO作业的时候我们用的 JavaDesign
嗅探工具吗?它可以静态检查你的代码质量,嗅到你的代码中的一些编写方面的“异味”。在刚开始的几个单元中,我的LCOM和FANIN和FANOUT等参数都颇为惨败,一条看下来几乎全是0,实力演示了如何将Java写成了面对过程的语言,有图有真相:
第五次作业(电梯单元第一次作业):
明显可以看出参数饱满了许多,暂且不说可能依然存在这一些问题。
我的面对对象学习之路大概是这个顺序:
- 用面向过程处理面对对象,特别体现在第一单元的多项式中
- 用多个类的面向过程处理面对对象,并开始接触Java设计模式(第二单元的调度器的单例模式算是我设计模式的入门)
- 用分责的方式入门面向对象,将每个对象都看作是一个只管自己的主体,用调度器或中间物去平衡调节,而不是每个类只存储自己的数据
个人感觉在面向对象的学习过程中,阅读他人的优秀代码是非常有必要的,比如第一单元优秀作业中hdl的代码,让我恍然大悟,原来代码还能这么写,虽然当时没有看懂为什么一个c语言一个类可以写下来的东西别人开了那么多的类。现在整个四个单元结束之后再去看,发现其实和现在的自己想的差不多,都会才用到继承或接口去完成一些共性的东西,而不是像自己之前写的一样限定住自己。
总结自己在四个单元中测试理解与实践的演进
今年全新的基于实验网站的互测确实给大家带来了惊喜,特别是第一次作业的时候刚刚接触到这个东西,而且大家的代码非常混乱,漏洞百出,所以大家都会去尝鲜式地,不遗余力地hack他人,经常出现一屋子狼人的情况。而很快可能经过三周的洗礼,进入第二单元的时候大家互测的热情明显下降,加之多线程的某些bug难以复现,可能只是卡时钟的问题,所以hack数量明显减少。越到学期的后期随着大家的学习压力增大,已经没有太多的精力每天hack别人的代码了。
我在自我测试和他人测试中总结了以下几点:
- 自己构造样例+样例自动生成+互相分享样例
- 注重对错误和异常的处理:很多同学可能会处理错误但是忽视了异常的存在,或者说用无脑全局
try{} catch(){}
来试图掩盖自己的错误,但是异常应该是自己在编写代码的时候能够有意规避的,比如很多人遇到的空指针异常,在Java里面算是最常见,很大情况下就是因为对输入的数据想当然地认为是正确的形式,或者说没有判断错误形式的输入。一个从学习c语言开始就需要养成的良好的自我检查习惯,比如对于指针类,在对其进行操作的时候就需要if (p != NULL) {do sth;}
- 善用JUnit单元测试。虽然我只在第三单元中使用过一次,但是体验还是不错的,但由于其输入的初始化没有直接输入方便,电梯后面的单线程作业我都是用debug调试,这里还是要夸intellij的ide还是真香的。
- 针对自己写的方法逐步测试,达到及时止损的目的。在最后一个单元里我深刻体会到了一边编写代码一边进行测试给自己带来的好处:前三单元我都是一次写完之后再进行整体的debug,这样就导致所有的bug你需要一起de,然而同样是错误输出有时候是初始化的时候错了,有时候是传值的时候错了,有时候是递归的时候出错了,有时候甚至是几个错误发生在一起,这些错误导致我们难以定位,所以每次写完一个函数或者少量的函数就对其进行测试,无论是输出测试也好还是debug的观察也好,每次保证写过的都是对的之后整体就不会有太大的bug了,有点类似于贪心算法吧。在最后一个单元的第一次作业中,我因为对解析出来的UmlXXX不是很熟悉,导致有的association没有联系起来,然后当我写完addAsso的函数之后进行简单的测试就发现了这一点,立刻查阅指导书和自己画uml导出测试,及时修复了这个bug,没有浪费太多的时间,我想要是最后再处理的话,我可能不太会怀疑是输出的地方出现了理解的偏差,而是怀疑在继承关系上或者是其他地方出现错误。我个人认为这就相当于人工的“单元测试”,他不是JUnit一样写完之后对单元进行选择性的测试,而是在编写过程中对新加入工程的单元进行单元测试。
总结自己的课程收获
毕竟这个学期一半的时间都奉献给了OO这门课,在所有考试结束之后还在写OO的总结,或多或少都学到了一些。在最后一个单元的时候,我就开始问自己:这个学期忙忙碌碌地打码,到底学到了什么?我想是先对自己然后才是这门课有一个交代。所以顶着当时的复习压力,还是没有应付式地用面向过程的写法混过最后一个单元,而是慢慢构思,再具体实现,可以自豪地说是自己这个学期写的最自我满意的一个单元。还在最后大家都特别忙的情况下帮助大家梳理了最后一次作业如潮水涌来的各种UML的类,帮助到了很多同学,获得了全站最高的关注度,颇感欣慰,也算是自己对OO这门课做出的微小的贡献,希望OO越办越好,发现自己对Java这门语言有了一些兴趣,希望明年能当上助教吧。
据说OO之后衔接的就是软件工程课,希望自己的知识在软件工程课上能有所发挥,也希望学习到更多有用的知识。
立足于自己的体会给课程组提三个具体改进建议
首先,一定要肯定zsa学长和其他助教和老师们为这一届的OO,甚至可以说为北航的OO,这一门6系专业hard-core专业课做出的贡献,贡献之大是毋庸置疑的,无论是从往届学长的描述上还是知乎上对这门课程的讨论,再对比我们所体验的OO课,可以说是全新的风格,更加人性化的,更加刺激的,有趣的体验!为整个OO课程组和助教团队打CALL!
既然要求我们写三个具体的意见,那我还是恭敬不如从命,在这里斗胆写下三个,这三个也是如果明年的这个时候我能够成为OO助教的话,希望给学弟学妹们带来的改进:
- 首先我自己的一次不愉快的经历:对于修复BUG这个全新的机制中的同质和非同质bug的判定希望可以更加清晰,希望在提交同质BUG合并修复的时候助教可以多写一些理由,而不是对大家都是简单的一句“代码修改过大,建议非合并修复”,这种除了助教没人说得清楚的驳回理由实在让修复bug的同学费解,给人一种助教没有仔细查看的错觉,这对于努力修复bug的同学而言无疑是非常打击的,所以希望能多一点理由,给同学们一个交代。此外,希望下一年能明确一下通过申请合并修复的bug的审查时间,不至于出现助教在ddl前驳回我们却不知道,等我们知道的时候修复已经关闭的情况。听说明年会接入微信的通知,这样应该会友好很多。
- 指导书的撰写希望能够有助教和老师之间的沟通和校对审查机制,希望可以做到指导书出版之后尽量减少实质改动,有补充说明的在讨论区中进行。本学期的作业可能是因为任务量比较大,无论是对于助教还是同学们,所以可能出现指导书发布之后存在一定的缺陷,而导致有指导书版本的迭代。指导书的迭代不是每个同学都会看到的,导致down了旧版指导书的同学会出现写了很久结果是叙述和理解出现了偏差的情况,才发现是因为指导书更新了。所以希望下年的Oo课程组可以提前做好准备和校对,直接发布LTS版本的指导书,方便同学们的查阅。
- 希望加入寒假时候的引言部分,给出一些简单题目的面向对象写法给同学们学习参考。 今年寒假的时候虽然有OO_homework_pre 但是其实很简单的题目同学们大多数还是用面向过程的方式实现,只是对java这门语言有了一定的了解而不是对面向对象这门课有一定的了解。正如zsa学长所说的,我们这门课的名字是面对对象,而不是JAVA语言学习,所以应该从一开始就以面向对象为目标,从开始第一个Project开始就引导思路,感觉我们寒假打的东西都太java语言了,激励大家查了几个转换字符串和整数的函数就解决了第一次作业。希望课程组可以寒假的时候提供一些面对对象学习的方向,算是对大家的自学能力的一种培养和训练,比如设计模式等,这样在大家第一个代码开始的时候就可以从面向对象的角度去思考了。
面向对象设计与构造,完。