面向对象设计与构造的第一单元,对“面向对象”的概念还根本不理解不熟悉,只觉得需要“分模块”,但不知道怎么分,分多少模块,怎么根据需要的模块的功能建立类。学习的进度又太慢,根本跟不上出作业的速度,导致三次作业的代码都写得很乱,很不面向对象。
1. 第一次作业
1.1 程序结构分析
第一次的作业比较简单,但由于刚刚接触面向对象,程序的结构仍然不太好。整个 project 只有两个类,主类和多项式处理类,且两个类的复杂度差别较大。由插件 MetricsReloaded 的分析可知,多项式处理类中的方法普遍有耦合度过高,判定结构过于复杂。主要的代码量都在输入处理部分,应该将其从多项式类中抽出来,组成一个单独的 InputHandler 类,这样能使层次结构更加清晰。
1.2 程序Bug分析
主要的BUG 集中在当结果为0时无输出和对于类似 'f' 这样的空白字符的处理。
第一个bug 的出现是因为不细心,忘记对输出进行处理,我用的数据结构是 HashMap,理应在输出字符串为空时输出"0"。又因为写好程序后没有对程序进行测试,完全依赖于评测机,面向分数,没有发现这个简单的bug。
第二个bug 的出现是对指导书的阅读不仔细,没有注意到指导书特别规定了空白字符只有空格和tab,也没有意识到会因为多实现了功能被攻击……
1.3 互测策略
主要依赖于读代码,读代码的读出bug的大头是正则表达式。但第一次很多人用了大正则,直接匹配整个输入,正则表达式的长度常常过百,导致虽然读到bug就百发百中,但效率极低。
2. 第二次作业
2.1 程序结构分析
第二次作业在第一次作业的基础上加了三角函数 sin(x) 和 cos(x),刚开始看起来似乎复杂了很多,但其实也只是公式复杂了一些,实现方法还是比较简单。
结构上和第一次相比多写了一个 HashMap 的 Key 类,因为有三个函数,就有三个指数,需要为HashMap 的Key单独写一个类。
结构上仍然存在耦合度高和判定结构复杂的问题,特定的几个方法过于复杂,很容易出现错误。
2.2 程序Bug分析
因为第一次不细心而遍体鳞伤后,第二次作业在写代码时尽量考虑到了能在输入输出处理上避免的所有bug,但还是出现了一个bug,尽管不是结构上的大的错误,也使我收到了一定的伤害。
这个bug是将项以'*'为分割符分开后,没有完全检测每一个因子的合法性,比如 “sin(x) * ” 这样的样例竟然被放过去了…
2.3 互测策略
吸取上次作业互测吃力不讨好的现象,我在评测区认真学习了大佬们提供的互测方法,写了自己的脚本,手动构造输入,自动对Room里的其他程序测试,获取输出,并计算比对。确实很大地提高了效率,但还是没有针对性地构造样例,又因为这一次大家都认真思考了,命中率不高。(我所在的Room 内一共只出现了8个bug)
3. 第三次作业
3.1 程序结构分析
第三次作业的难度提升了一个大的台阶,前两次对结构层次的要求都不高,毕竟只有两三个函数,+-和*两种组合方式。第三次作业加入了嵌套机制,我的第一想法就是用递归。
结构上刻意根据功能划分出了相应的模块,但层次并不分明,还不是很清楚接口的用法。
总体的复杂度做得比前几次都好,但仍然在表达式类的求导方法中写得很乱,基本复杂度、模块设计复杂度和模块判定结构的复杂度都高得不行,甚至derivation 原先是一个方法,出于方法长度限制得原因我把它分成了两个方法…
3.2 程序Bug分析
主要出现了两个Bug,一是指数的格式判断错误,忘记了指数可以带符号,于是对所有带符号指数都判断为错误。这是一个由粗心引起的致命的错误。
二是处理输入,读到 ’-‘ 号时把表达式分为两部分,应该对后面部分的表达式变号,在变号的过程中没有考虑到指数带符号的情形,于是又出现了一个致命的错误。
虽然两个bug 都出于题目理解不到位,但是如果我的程序更有层次,能划分出清楚的子类父类,特别写一个指数类,就很难出现这样的错误。总的来说还是结构引发的错误。
3.3 互测策略
个人总结
1. 正则很好用,lookahead & lookbehind 很好用。
2. 善用接口能使代码变得结构分明,具有美感。