BUAA面向对象第一单元总结
目录
一、度量分析
- 第一次作业
- 第二次作业
- 第三次作业
二、Bug总结与反思
三、Hack策略
四、对工厂模式的思考
五、对比与心得
引言
第一单元共有三次作业,都围绕正则表达式的求导与优化展开,三次作业呈现递进式关系,每一次作业都在前一次的基础上增加需求,难度由浅入深,着重考察了学生能否应用面向对象的思维来解决问题,合理运用继承逻辑,对不同类的对象构造不同的容器,将相同或相似的功能进行归纳,并且鼓励学生尽可能地做到代码的可重用性,而不至于面对一次新作业从零开始。
一、度量分析
第一次作业
- 第一次作业目标为对简单多项式进行分析求导,项的内容只能是x幂次,并且排除了格式非法的输出,较为简单。
- 我的策略:
- 构造一个容器Term用来存储每一个x幂次项,有两个属性,分别为coeff、power;
- 将整个字符串在Main中先进行预处理,得到单个项后传入Term类进行构造;
- 使用HashMap来存储所有项求导后的结果,用power作key,coeff作value,好处是方便合并同类项;
- 我的优化思路:首先是合并同类项,其次注意到一些细节,例如1或-1为系数的项的省略,在有正项的情况下首先输出正项等;例如x**2省略成x*x;
- 利用另外的一个Typeprint类来实现性能更优的输出。
- UML类图如下:
- 度量分析如下:
- 优缺点分析:
- 缺点:可以从度量分析图中看见,我的程序复杂度几处爆红,这是由于我在第一次作业中(完全)没有参透面向对象的意思,而仍然在用面向过程的思维解决问题,导致代码模块耦合度过高,并且难以复用,事实证明我的第二次作业几乎是从零开始的;
- 优点:解决问题的步骤尚算清晰,每个类的分工较为明确。
第二次作业
- 第二次作业在第一次作业的基础上增加了三角函数形式的项、乘积项,并且需要处理除了空白项位置之外的格式错误;
- 我的策略:
- 首先去空白,判断格式是否正确,若格式错误直接输出WrongFormat并退出;
- 接着分析表达式,用PolyTerm类拆分项,并且把一个项中的各个乘积因子合并,构成axb(sin(x))c(cos(x))d这样的格式;
- 按照求导公式将上述格式下的项进行求导,并且仍然用HashMap存储结果;
- 构造Term类,属性有xpow、sinpow、cospow,作为HashMap的key,求导后的各个求导项的coeff仍然作为value;
- 优化:此次优化难度增加,主要是三角函数部分的优化,受教于讨论区同学提出的见解,我将得到的map进行遍历,并将sin、cos指数和相同并且sin(或cos)指数差为2的两项进行合并;
- 仍然用Typeprint进行输出;
- UML类图如下:
- 度量分析如下:
- 优缺点分析:
- 缺点:此次作业的优化类Optimize和输出类Typeprint都有复杂度上的问题,并且没有运用面向对象的思维解决,本质上还是把这题当成了C程序设计的题目来做;现在想来,如果分别构造sin类、cos类、x幂次类和项的容器将会很大程度上减轻代码复杂度,并且方便下一次作业的扩展复用。
- 优点:类之间有明确的分工。
第三次作业
- 第三次作业由于增加了表达式的嵌套,难度上有了质的飞跃;我边构思边写整整两天还没实质进展差点以为要产生第一个无效作业;
- 我的策略:
- 首先进行简单格式检查JudgeLegal,此次格式检查工作我没有像上一次那样压缩在一个类里完成,而是又在整个解析表达式的过程中加入了ExpJudge,一旦发现格式不对的点,立刻输出WrongFormat并退出;
- 构造抽象因子类Factor,包含求导Derivate和输出ToString两个核心方法,再构造SinFac、CosFac、Constant、XpowFac和ExpFac几个具体因子类来实现;
- 用ParseExp类来解析表达式并拆分项,再用Term类来处理拆分好的项;
- 遇到嵌套的表达式因子,重复上一步骤,直到不再是表达式因子类而是其他简单类;
- 由于能力不足时间又紧,我放弃了乘积的优化。
- UML类图如下:
- 度量分析如下:
- 优缺点分析:
- 缺点:ParseExpre和Term类里功能有些冗杂;没有成功运用工厂模式进行因子的建立;
- 优点:有了那么点面向对象的意思;
二、Bug总结与反思
- 第一次作业较为简单,强测与互测均没有出现bug;
- 第二次作业出现了两个bug:
- 由于在输出时的一个if语句没有处理好,导致sin(x)与cos(x)前的负号出现问题,强测没被逮到,互测被hack了;
- 优化时没有考虑新生成的项在map中已有同类项问题,强测也没被抓,互测被hack到了;
- 第三次作业在处理WrongFormat判断上有遗漏的地方,强测终于抓个现行,互测反而没被hack到;
- 总结:可以看到,我的bug都是发生在细节处的处理,可见我在编代码过程中还缺少一点全局分析,程序面对特殊数据的处理还不够强大,这也是我在日后作业中需要注意的地方。
三、Hack策略
- 第一次作业中只hack成功一名同学对+-的处理问题;
- 第二次作业中hack到一名同学和我一样的负号处理错误;hack到其他同学的求导错误问题;
- 第三次作业hack到优化超时问题、求导错误、输出格式错误问题。
- 说来惭愧,第一次作业刚开始时就有好多同学自己搞了评测机来生成数据和对拍,当时我研究了好一会还是由于不熟悉脚本编写等问题而作罢,在几次互测过程中,我只能靠读代码和猜测bug来手编数据,hack过程还是十分艰难的;
- 我对互测的理解是:互测不是我们加分的工具,而更应该是我们学习他人代码、理解他人优秀的编程思维并化为己用的过程,如果仅仅是黑箱盲测,那反而有点错失这个过程的意义所在;
- 当然,评测机还是要造的。
四、对工厂模式的思考
- 我第一次认识工厂模式这个名词是在pre过程中,不过当时只能在网络上搜索工厂模式的一些代码来理解它,还没能明白为什么要运用工厂模式以及在什么情况下可以用到工厂模式;
- 在面向对象这门课的理论授课以及实验课过程中,我逐渐了解到工厂模式的用法和优点,例如易于扩展、方便维护、解耦合、设计者仅仅需要关心工厂方法返回的接口方法,不必关心实现细节;
- 以第三次作业为例,我可以构建一个因子工厂,在识别出因子类型后,直接用工厂来new相应的对象,可以大大降低代码复杂程度,也方便之后的复用和扩展。
五、对比与心得
- 首先要早日抛弃惯有的面向过程设计思维,不要把类仅仅当作不同步骤的实现,那样说到底仍然是一main到底思维;
- 在之后的第一次作业中,就要尽量预想到之后扩展的部分并留下扩展余地,尽量多想想怎么更好地面向对象,回归课程要求;
- 遇到难以下手的问题不妨去讨论区多看一看问一问,dalao们都不会吝啬分享他们的思路;
- 互测过程不能弃大舍小,不要只想着怎么构造数据去发现别人的bug,而要更多地学习他人的优秀代码。