buaa_oo_第一单元总结

前言

  本单元面向对象作业是展开带括号的表达式,并对表达式表示形式进行化简。由于是第一次接触java,寒假中的pre练习在开学前两天才草草完成,而且开始时没有学会git的用法,不知道怎么提交评测,感觉心力交瘁。第一单元碰到这一系列问题时总觉得题目很难很复杂,心里感到害怕,觉得自己完成不了。虽然经过了助教,老师,同学的点拨,也下载了其他同学的代码,至始至终没有搞明白表达式到底应该怎么解析,不明白为什么运算也能算“类”并且和一些数字,变量等共同承接自一个接口,不明白到底是怎样一个逻辑,于是这个单元的三次作业都用的预解析方法。虽然使用预解析方法对于分数和自信上都对我有所打击(觉得自己是个垃圾),但是我还是在预解析方法下逐渐感受到了面向对象建立不同的类,分工协作等抽象思想。

第一次作业

UML

缺点:对于项的考虑欠缺,项有系数,符号是应该的,但指数对于项来说不太合适,只是因为第一次实验中因子较少,变量只有x可以这么表示,在后续作业加入三角函数因子等就不合适,应该有一个更小的类factor,而项term应该表示为其包含因子的乘积。除此之外,用来判断是否有变量x的标志variable似乎是多余的,可用指数为0表示常数,即系数*x**0,指数不为0表示有变量x,总的来说对于类的属性设计还是欠考虑,看起来非常奇怪。对于输出表达式,将这个方法仅放在了Expression类,与面向对象分工协作和抽象思想相悖,在后续的重构中将其放在了Term,Factor中均有,Expression只需遍历每一项,调用Term的print,Term遍历Factor调用其print,这样做无论是处理共性输出还是考虑特例都更合适。

 

架构

    第一次作业的思路是将预解析读入的每一行表达式用Arraylist储存起来,并储存其对应的表达式号id,在后续行读入的式子中出现该表达式id,则从Arraylist中找到该表达式进行运算,输出结果则输出最后一行对应表达式即可。如何表示表达式及输出呢?我的思路是将表达式分为若干项Term的和,在代码中则表示为Expression类中包含一个Term的Arraylist,项是最基础的元素,其包含系数coefficient,变量标志variable,指数index,符号char。对于可能出现的加减运算,视为两个表达式包含的所有项即Terms加到新的表达式的Terms list中。对于乘法和类似的乘方运算,视为表达式中的每一项与另一个表达式的每一项循环相乘,这个方法在Term类中实现,最后把每一个得到的新项加入到新表达式的Arraylist中,完成乘法运算。

化简优化思路

  第一次作业关于项内的优化较少,重点是表达式一级的合并同类项,思路是遍历表达式中的每一项,对每一个项循环遍历其后项,如果二者指数相同,则进行系数合并等。

复杂度分析

 

 

  ev(G)基本复杂度是用来衡量程序非结构化程度的,基本复杂度高意味着非结构化程度高,难以模块化和维护。

  Iv(G)模块设计复杂度是用来衡量模块判定结构,软件模块设计复杂度高意味模块耦合度高,模块设计复杂度不能大于圈复杂度,通常是远小于圈复杂度。

  v(G)是用来衡量一个模块判定结构的复杂程度,圈复杂度大说明程序代码可能质量低且难于测试和维护。

    我在主类的main方法里杂糅了很多输入处理及输出,用了较多的循环和if_else,在表达式类里也有很多if_else。思考过后可以通过将其拆分为多个方法在主类中调用优化更好。

 

 Expression和主类里都用了很多条件判断和循环判断语句,造成复杂度很大的问题,应该尽量将方法进行拆分。

bug修复

  强测中出现了一个溢出错误,最终发现是因为虽然用了BIginterger但是转化成Biginterger时对java理解不够,用的idea提供的方法,发现提供的是先转成int再转成的Biginterger,给个Long类型都会爆,这个错误属于检查不够充分导致的。互测时被hack了6次也都是因为这个溢出问题。该bug出现在Expression类的Expression构造器中,该构造器方法考虑的特例较多,方法较长,不同情况都有不同的类型转换,出现了疏忽。

第二,三次作业

UML

 

 

 

  由于使用的是预解析方法,第二三次作业在类的设计和架构上没有太大的变化,只有一些细节处的调整。最大的改变是加入了Factor类,Factor类作为最小的变量代表各个因子,如常数因子,三角函数因子,幂函数因子等,用type变量表示这些因子的种类,其下含一个类Expression的变量,便于对三角函数内可能存在的表达式变量进行操作和输出。类Term包含Factor的Arraylist变量,逻辑上其为包含的各个因子的乘积。

  优点是相比于第一次作业,终于清晰的表示了表达式,项,因子间的关系,起码是可以满足要求了。缺点是对于各因子,各项,各表达式之间的关系还有待商榷,比如在化简时,必须的如何判断两个因子相等时用简单的type和指数等并不够,对于幂函数和常数可以,但对于三角函数类便不行,三角函数内的expression类也要相等,当时认为这是一个死循环,因为判断表达式相等就必须判断项相等,判断项相等就必须因子相等,本来这只是一个递归过程很好解决,但我却鬼迷心窍先去考虑Term能否化简,发现不太行就放弃了继续考虑,简单的用表达式名字是否相等来考虑,这就导致了我判断两个因子是否相等时非常非常的不全面,甚至对最简单的sin(x)和另一个sin(x)都无法判断其相等,因为他们内含的表达式x对应的对象不一定是同一个对象,可能是程序两次创建的,这就非常愚蠢,恶性循环导致合并同类项非常不全面,最终导致性能分非常的低,现在想来真是懊悔不已。

 

架构

  在第一次作业的基础上迭代进行。对于加减运算,还是将两个表达式的各项直接加入到新表达式的Terms数列中;对于乘及乘方运算,将两个表达式的每一项相乘,对于一项乘一项来说。就是将每个项的所有因子加入到新的项的factors中,再把得到的新项加入到新的表达式中。对于三角函数运算,则生成一个新的因子,这个因子的expression变量置为这个表达式,再将这个因子加入新项,新项加入新表达式即可。对于输出模块则依次调用表达式,项,因子的输出逻辑,不同类内递归调用各司其职,个人感觉这一点最为符合本单元设计思想(如果不用预解析方式应该是解析表达式吧)

复杂度分析

  ev(G)基本复杂度是用来衡量程序非结构化程度的,基本复杂度高意味着非结构化程度高,难以模块化和维护。

  Iv(G)模块设计复杂度是用来衡量模块判定结构,软件模块设计复杂度高意味模块耦合度高,模块设计复杂度不能大于圈复杂度,通常是远小于圈复杂度。

  v(G)是用来衡量一个模块判定结构的复杂程度,圈复杂度大说明程序代码可能质量低且难于测试和维护。

复杂度高的几个方法基本是与化简有关的方法,和我上文提到的我没有想清楚化简的逻辑只是草草做了特判有直接关系,本应可以避免。

类复杂度分析

 

 OCavg代表类的方法的平均循环复杂度。OCmax代表类的方法的最大循环复杂度。WMC代表类的总循环复杂度。

我在主类里杂糅了很多输入处理及输出,用了较多的循环和if_else,在表达式类里也有很多if_else。思考过后可以通过将其拆分为多个方法在主类中调用优化更好。

 

第二次作业bug修复

第二次作业强测没有出现问题,但是互测时被hack了三次。发现是在代码迭代时化简及输出的逻辑出现了问题,在迭代时只简单进行了复制黏贴没有考虑由于加入了新类导致的情况变化,导致有些项并没有合并完全,在项系数为0的特殊情况下,这些项并没有被移除,而我的输出逻辑默认其是被移除的,导致输出出错。这是测试不充分导致的。该bug出现在Expresssion的simplify方法,该方法行数较长,又用了多重循环,在循环内部又有多种情况脱离循环,逻辑较为复杂,出现了疏忽。

第三次作业bug修复

第三次作业强测没有出现问题,互测时被hack了七次。这个错误反映了我态度极其不端正,因为出现这个错误的原因是官方包导入忘了修改,我在阅读了第三次作业说明就开始在第二次作业基础上做一些小的修改进行一些小的测试发现没有问题,中测也没有问题后就去做下文提到的冯如杯项目等了,然后互测时发现一直被hack,看了看才发现是官方包导入那行没修改,应该高度反思。

如何发现别人的bug

  其实写有关这个问题,我又感到十分的羞愧,下载别人代码时由于我用的是预解析方法,对别人的代码结构看的很吃力,而我下载下来的主要目的是学习解析表达式方法和理解别人这样写的思想,上文也提到花了很多时间阅读代码,阅读的时候我默认代码是正确代码看的,理解就很吃力,找到bug就更难了,第一次我还尝试比对了一下,进行bug测试,后两次互测时我就基本放弃找bug,改为理解学习代码思想了,对于互测这项并没有做出什么贡献。

心得体会

   本单元的学习及作业完成情况是低于我的预期,很令我不满意的。其一是至始至终没搞清楚如何解析表达式,并非没有花时间在这方面上,请教了别的同学,互测时下载了别人的代码,研讨课听了同学的分享,也花了很多时间来研究,虽然看懂了别人代码的含义和执行的大概,但始终不能理解为何有这样一种思路,代码可以模仿,但没有理解思路的我却不能独立的复刻出这种代码出来,具体来说为什么每一个运算建立一个类,这些类和表达式,项,常数因子,变量因子共同承接自一个接口,这我完全弄不明白,思考了很久,只能理解到如加法运算这表达的是加减法的之后的结果也是一个表达式,通过这种方法还可以有left,right属性用来递归下降到最小的因子进行操作,但我还是不能理解到底是怎么写出来的,为什么代码要这么写,这到底是自顶向上还是自底向下写出来的,我也花了三四天的时间试图独立复刻出这个思路来,但是因为理解不了这种思想或是不知道这种思想如何指导编写代码,导致我编写时非常的迷惑,经常不知道下一步应该写什么,是创建一个新类,还是将方法或属性加到某一类?对于细节的处理也很糟糕,因为我完全不知道考虑到了哪里还有哪里不足。3,4天的尝试最终在DDL的压迫下放弃了,只能采取预解析的方法,直到第三次作业结束和现在的博客作业我都未能完全理解如何解析。对于这一点,我觉得可能的原因可能有:1.第一次接触java,对其特性不是很熟悉,不知道怎么将思想转化为java代码。2.没有看往届学长博客,课堂上又没有进一步涉及处理表达式,只是疑惑于为什么周边的同学都会解析方法而我不会,觉得自己是个垃圾,以往有关编程的课程都有纸质书且课堂上会给出类似的样例程序,找不到参考资料,暗自神伤放弃。

  其二是在pre和初期并没有搞懂git到底怎么用。在假期看指导书就云里雾里,装了VPN软件简单看了看就放弃了,提前两天返校的时候才做pre,感觉没几天了很急,git没看懂到底怎么用(后来我认为还是指导书对git设置的表达有些问题,不知道自己进行到的那个步骤到底代表了什么,而且有关git的操作有三处,一处VPN设置,一处git,一处idea),看的时候不明白到底怎么算成功,问题出在哪里,关键步骤又跳跃了一下,最终我选择暂时放弃,先把题目代码打完再说。后来尝试的时候发现卡在了输入对应密码上,一直提示密码错误,当时又不清楚输入密码命令行没变化是为了保护隐私,不确定到底是输入还是没输入,改了很多次密码输入还是不对,又暂时放弃了。第一次实验的时候问了助教,但这个密码问题还是没能解决......请教了别的同学和自己探索,别的同学也有这种情况解决不了,最后提交代码的方式变成了手动进git网站按上传和replace,好蠢的方法但暂时解决了燃眉之急,后来在OS课程上学习了git的使用,也算满足了课程要求的对于git的理解,但OS是在虚拟机上,自己的电脑的git还是卡在输入密码那个步骤,还需要继续的请教和自己探索了。虽然这个问题和代码编程没什么关系,但是导致了我起初对这门课产生了畏难的心理,进而出现了抵触,再然后变成了没信心,对放弃解析选择预解析也有一定的影响。

  其三是对于化简优化的处理,在第二次作业时由于考虑了很久解析方法,对预解析下的化简方法没有什么时间考虑,又有上文提到的陷入思维怪圈草草放弃。第三次作业原本认为时间充足,但开了次班会发现时间与展示课程有冯如杯的硬性要求,而冯如杯参赛迫在眉睫,又不得不抽出时间去做冯如杯和新的操作系统实验等,导致我本来灵光一闪想到的解决这个思维怪圈的方法却草草放弃没有去做,现在想来真是后悔,应该是一个很简单的问题也能极大幅度改善性能。除了时间因素以外,还有心理因素,害怕,畏难觉得有很多java内容和特性理解不透彻,怕改了以后导致更多错误,对自己没有自信觉得不行,想来真是糟糕。

  其四是对java特性理解不够。以前只有C的基础,对java各种变量引用完全不明白,很长一段时间才发现引用好像有点像指针......对于java对象的方法应用不太明白,比如还是没弄明白为什么.clone()这个方法一直用不出来,想用clone的办法避免改动变量时对其他变量改动,但却一直报错,迫不得已放弃后总是出现改动这个对象时另一个也变了,产生bug令人很困扰。类似的问题还有一些,如提到的化简时.equal()办法要自己的类里重写复用花了很长时间意识到等。

  困扰的问题很多,希望通过后续的学习能慢慢解开这些问题。

posted @ 2022-03-25 19:55  buaa_zzy  阅读(17)  评论(1编辑  收藏  举报