菜鸡谈OO 第一单元总结

时间:2021-01-08 02:00:03

“OOP永远是我的好朋友爸爸!”        ——来自某无能狂怒的菜鸡

  身处在OO的第一个摸鱼黄金周中的我,感觉到了巨大的满足感。如果写博客这种充满意义的事情可以代替我们亲爱的作业,那么我提议每周来两个:)下面开始正经分析~

Homework 1  简单多项式求导

  • Part1 程序架构分析

  面对人生的第一次面向对象程序,没有学过java的我认认真真思考了整个代码框架,最后通过不懈努力,完完全全写成了面向过程(毕竟我又没得对象)。

  在这次作业中使用了三个类:PolyDerivation、Poly、Term。程序入口(main)、输入、格式检测都被我堆砌到了PolyDerivation的类中,导致PolyDerivation的功能不明确,但是有着大量的方法。当然值得一提的是,我采用了预处理的手段简化了很多判断问题,也有效的避免了大正则爆栈。Poly是多项式类,主要用了指导书中的HashMap数据类型保存了每一个单项,并切割将每一个项放进Term类。Term类包含了degree和coeff两个成员。可能这个切割Poly是我唯一的面向对象吧。(逃

  本次作业的求导本身只是简单的大数相乘,问题不大。但是由于我不知道重写toString方法,导致写了80行的大条件语句实现了output。又由于checkstyle问题*拆成了两个方法。这真的有种造*的感觉!

  本次作业的类图和复杂度如下。

  菜鸡谈OO 第一单元总结

  菜鸡谈OO 第一单元总结

  从这个类图中,不难看出我采用的是直线式编程呢个,一条路走到底。好处在于简单好写不易出错(当然是针对我这种菜鸡而言),坏处是各个类不够简单,耦合度有点高,面向数据可拓展性基本为0。

  • Part2 程序bug分析

  由于我的面向过程程序写的很好:),本次强测和互测均未被发现bug。其实还是第一次作业要考虑的情况比较线性,使用这种面向过程思维,只要处理周到,还是不会出很多问题。

  • Part3 发现别人的bug

  在OO为我们提供的狼人杀游戏中,我充分认识到了狼人的可怕之处。夜半惊魂,起身刀人。

  第一次作业的bug基本上都集中在鲁棒性测试上,也就是利用他们漏判\v,\f等空白字符,找到了很多bug。另外就是*问题。但是没有找到太多功能性bug。

Homework 2 带三角函数的简单表达式求导

  • Part1 程序架构分析

  第二次作业增加了三角函数。虽然我们都猜到第三次作业可能就是嵌套求导。但是懒惰战胜了理智,我还是没有选择重构代码。我选择了一种错误率最低,优化的可操作性最高的三角函数通式法。也就是把所有的项都表示为(a,b,c,d) 的四元组形式。用ArrayList的数据结构存储项(其实是不会HashMap的重写equals方法)。

  由于没有选择重构,本次代码还是三个类,也就是在第一次作业的基础上,修改了derive方法,求导也只是(a,b,c,d)的数字转化。反思一下,这其实是面向数据编程,是不太可取的。事实上,我也为第三次作业付出了代价。个人认为最好的方法其实是讨论区大佬所说的写两个版本进行对拍,一个版本为了这次优化,另一个版本为第三次做准备。可惜我太菜了,也确实没有这么多精力。

  优化方面,由于能力有限,我只做了一些简单的sin(x)^2+cos(x)^2和合并同类项的优化。

    菜鸡谈OO 第一单元总结

    (死亡复杂度预警)高复杂度来源于poly中大量的条件输出和优化方法。

    菜鸡谈OO 第一单元总结

这一次作业几乎和第一次一样,只是把幂函数的两元变成了四元,缺点还是耦合度高,几乎没有可拓展性。

  • Part2 程序bug分析

  第二次的强测没有发现bug,但在互测中被发现了负指数幂的bug,因为在程序中自己手抖把去除第一个负号写成了replaceAll(“-”,“”)。。。导致了整个项中的负指数都被我去掉了。这只能说明我的本地测试没有过关,像这种比较好发现的bug却没有发现。bug修改也很方便,直接String.substring方法处理完事。

  在这周我也见识到了自动测试的无比威力,通过随机化测试,应该可以避免绝大多数类似我这种功能性bug。这也是今后自己需要补充注意的。

  • Part3 发现别人的bug

  经历了第一次作业的狼人杀,我发现大家针对恶意输入鲁棒性测试的处理能力大大提高了,具体表现为用鲁棒测试很难测出bug(魔鬼)。但是我仍然构造了含大量空格和\t的测试样例并成功找出了bug。即sin(x)  *     cos(x)       *     +      1         这样的数据。

  另外一个大面积出错的点在于缺项数据的处理,如sin(x)*、cos(x)+(然鹅我自己居然在第三次作业里挂了这个点,哭了)

Homework 3 终极无敌嵌套表达式求导

  • Part1 程序架构分析

  老实说,虽然在之前已经猜到了嵌套求导,但是看到这个指导书的那一眼,我内心是拒绝的。我完全没有想到接下来的两天我能完成这个project。(并且还要学习OS)然而狗急跳墙,人急了原来也是能写出OO的,虽然写的很烂。

  第三次作业宣告了我前两次作业结构的完全颠覆。也就是说再也没有所谓的通项式求导,唯一剩下的只有基础求导规则和因子求导。我苦思冥想着作业架构,咋也想不到,于是就出去看了场电影激发灵感。

  困扰菜鸡的第一个问题就是怎样进行格式检查。由于我在前两次作业中都是采用了预处理+大正则的手段,而这一次这种递归检查用大正则我确实是做不到的。那么第二个大问题就是求导。而格式检查和求导本质上其实都是递归的过程。所以在本次作业采用了递归下降的思路,将一个多项式按照加法分割(利用堆栈进行括号检查),分割成项之后送入项类。项按照乘法分割,分割成因子之后送入因子类。因子包括四类基础类型:Num、x^a、[sin][cos](x)^a、(表达式)。其中表达式又被送入表达式类,形成了一种递归的流程。表达式、项、因子这些类中都包含了各自的分割split()、格式检查check()、求导deri()的方法(后来想想我是不是应该建立接口抽象方法???)这是笔者自认为三次作业中最面向对象的一次,也许这就是潜移默化被逼出来的进步吧。但是这次课传授的继承和接口我没有灵活运用进去。对于继承和接口只是模糊地有一定概念,但是完全不会用。

    菜鸡谈OO 第一单元总结

    类一多起来,死亡复杂度果然直线下降。

    菜鸡谈OO 第一单元总结

  稍微面向对象一点的作业,Factor和Item类由于存在大量循环和判断,复杂度还是很高。我认为如果将Factor再细分成xFactor、sinFactor、cosFactor、expFactor也许会大大降低复杂度,也能增加可拓展性。

  • Part2 程序bug分析

  本次强测挂了一个点,就缺项输出WF。这里我就要谈一谈split的一个坑点,split会自动丢弃末尾空值(????)。强测的最后一个点2 * (sin(x)) *(x)*sin((x))^+1*x*cos(x)^+2*cos(x)*(((x)))*-1*,也就是缺项检测,末尾空值被自动丢弃,甚至都没有判断空串的机会。。。于是就这么心安理得地挂了。

  互测中由于被禁止了WF输入,我这个bug没有被hack。而又因为这次我完全没有优化!!!所以输出数据稳得很,功能性bug也没有被发现bug,所以是0hacked。

  • Part3 发现别人的bug

  第三次的互测禁止了WF输入,也就是说鲁棒性bug完全被杜绝了(其实还可以让别人的程序误判WF),只剩下了功能性bug。本以为互测屋bug数会大量减少的我,经过测试发现功能性bug也不少。功能性bug主要有两类:一类是优化过程中出错、一类是递归下降处理不当。

  具体来说,优化出错通过大量随机测试其实不难发现,而sin(((((((((((((((((((((((((((x)))))))))))))))))))))))))))的式子可以检测是否TLE,另外我们room中还有同学漏判了\t和^+的情况,也是很不幸成为了大礼包。

  • Part4 第三次作业的反思

  由于第三次作业很难,所以part4是我对第三次作业的一些反思。可以看到的是,在第三次作业中,我没有再把诸如输入处理和格式判断塞到Main中去,而是分成了Input类;也没有再一遍遍手写WRONG FORMAT!而是写了一个Error类,可能这也是一些微不足道的面向对象吧。本来是应该再写一个Output类进行优化和toString()的,然而这次作业的优化已经超出了我的能力范围,所以就此放弃,把输出工作也直接交了各自抽象层次。

  具体来看,我把整个多项式的处理抽象成了四层对象(即使我还是不能很好地理解形而上的抽象),四个对象之间的关系处理是让我很头疼的一部分。因为Exp,Item,Factor,Num并不是简单的包含关系,他们其实是PPT最后一页的那种彼此纠葛相爱相杀的关系,然而当时的我是菜的看不懂的:)。这种层次化设计我认为是需要细细体会的。我在这次作业中完全没有用到继承和接口,就是因为我几乎没有对面向对象的这种“获取数据处理的归一化能力”的思想产生概念。换句话来说,即使第三次作业我的确对表达式、项做了一定的封装,然而我的程序本质上还是面向过程的。。。。不说了,我去看大佬的代码和thinking in java恶补了。

创造模式思考和代码重构

  代码重构只针对第三次作业。(第三次其实也包含了第一二次)

  重构思路:数据层面抽象管理使用继承,非数据使用接口或继承。重点参考PPT实现。

菜鸡谈OO 第一单元总结

  创造一个Factor父类,子类xFactor、sinFactor、cosFactor、expFactor。创造一个comFactor父类,子类add、multi。multi规则产生原来代码中的Item,add产生exp。值得注意是递归过程,也就是expFactor会被送回到add规则中。add和multi通过给自的分割符对整个加法项和乘法项进行拆分送回Factor。

  此外定义接口deri和check,每一个Factor有其对应的求导规则和格式检查规则。Input和Output分别用于输入预处理和优化输出。

  这样的代码结构应该会大大降低代码的耦合度,也会更具有面向对象的抽象思维。

  至于创造模式(Creational Pattern),本菜鸡还确实是在上一周的OO课上才知道的名词。现在只能说不明觉厉,比继承和接口又上了一个抽象等级:)学习了 一下之后,感觉可以通过使用抽象工厂(Abstract Factor)进一步增强代码的可拓展性。具体就是建一个Factor工厂,把基础因子xFactor、sinFactor、cosFactor、expFactor都塞给工厂。由工厂类统一管理创建和调用过程,这样封装得也就更加彻底,今后新建因子类只需要修改Factor。建造者模式(Build Pattern)好像完全不会用,应该是用来调度整体对象(???)。不知道自己理解的对不对,希望大佬指教。

想说的话。。。

  三次OO作业不仅让我快速学会了java语法,也领悟到了面向WF鲁棒性的重要性。这是我第一次运用工程化思想,以前的题大多都是给出固定的input->利用一些算法和数据结构得到结果->进行输出。而这也是典型的面向过程编程。作为一个码代码能力低下的菜鸡,要在短时间内顺利完成从面向过程到面向对象的过渡,并写出动辄几百行的代码,其实是很吃力的。所以三次作业三周,我也几乎花费了大量的时间在OO上(救救孩子的OS)。我也确实感觉到了自己的进步,尽管可能还不够多。所以说,OO界面的毒鸡汤也还有几分道理??不经巨大的困难,不会有伟大的事业:)

  自勉自勉~