第一做业主要是给咱们引入了一个对于非法输入处理的思想,包括第一次上机,都一直围绕着一个全新的主题,就是非法输入处理,而对于此次做业自己,其实难度并非很大,甚至用纯C也不会有很大的工做量,可是引入了一个这样一个重要思想笔者以为是学到不少的git
提及第一次做业,笔者实际上是有为第二次做业作准备的(可是看起来准备的并不合格),在第一次做业中笔者还专门准备了一个虚基类来准备继承其余的项(由于怕操做失误删了),以方便在第二次做业若是加入其余的函数,就直接加上一个新的子类,而后上层逻辑用利用多态,根本不用改(事实证实是我想少了)。算法
直接上UML图吧(先让我去git clone 一下)编程
能够看到,看到每一个类的方法不少,不过细看能够看出来不少其实这主要是方法模块化的结果。设计模式
首先是关于错误处理和输入规范化,笔者单独实现了一个Parser解析类来完成这个工做,利用正则分割和匹配每一项,在遇到非法输入即抛出异常,有顶层类捕获并输出WF架构
而后就是求导部分,由于只有加法,因此只须要表达式调用每一个项的求导便可完成求导模块化
最后是化简,也就是优化,在本章做业里,优化都是一个让我以为很难受的地方,也不必定说每一个优化有多么复杂的逻辑或是算法,可是更多的给个人感受是,从整理到求导整个过程像是一个总体,而优化就像是没有关系的一件事情硬插在了里面。函数
不过如今静下心来思考,其实,优化不该该被放在任何一个因子、项、表达式类里面,其实,笔者以为应该装饰者模式来处理也许会更好,每种优化做为一个装饰者,而后将项、表达式、因子传入,而后再获得化简后的结果,这样的组织首先不会致使化简逻辑和运算逻辑混为一谈,两件事情高度耦合,错误就很容易发生(笔者本人也是深受其害,以致于Bug修复的时候只须要注释掉simplify()
函数就能够修复绝大多数同质Bug),因此仍是回到了高内聚低耦合的话题上,这件事情应该是设计阶段要仔细思考的一件事。oop
因此也能够看到,个人项和表达式里面充斥着大量的和项与表达式运算无关的方法,他们也严重违反了单一职责原则测试
Class | CBO | DIT | LCOM | NOC | RFC | WMC |
---|---|---|---|---|---|---|
Arithmetic | 3.0 | |||||
Parser | 1.0 | 1.0 | 1.0 | 0.0 | 21.0 | 13.0 |
Poly | 2.0 | 1.0 | 1.0 | 0.0 | 39.0 | 23.0 |
PowerItem | 2.0 | 1.0 | 4.0 | 0.0 | 26.0 | 29.0 |
Solution | 2.0 | 1.0 | 1.0 | 0.0 | 16.0 | 3.0 |
Total | 68.0 | |||||
Average | 1.75 | 1.0 | 1.75 | 0.0 | 21.0 | 17.0 |
从度量中也能够看到总体复杂度其实仍是不高的优化
第二次相对于第一次,求导规则更加复杂,数据如何组织是一件比较麻烦的事情,因为当时笔者没有将常数做为单独一项来处理,因此致使在优化合并的时候,提取系数极其困难
直接上类图,我们看图说话
很庞大,可是其实就是因子继承虚基类,而后全部承在一块儿的因子构成一个所谓的Box,Box中用HashMap来维护三项,主体逻辑跟上一次彻底相同,可是化简逻辑复杂了不少,从图中也能够看到有不少化简函数放在了里面,让人看不懂,这也是这三次做业的败笔所在,没有把化简功能分离出来,这样不只不利于测试化简功能,并且程序的未知行为没法估测,这也是这样测试中出现Bug的一个十分重要的缘由
下面给出程序的度量
Class | CBO | DIT | LCOM | NOC | RFC | WMC |
---|---|---|---|---|---|---|
Constant | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
ItemType | 4.0 | 1.0 | 2.0 | 2.0 | ||
Solution | 4.0 | 0.0 | 1.0 | 0.0 | 11.0 | 4.0 |
ItemFactory | 6.0 | 0.0 | 1.0 | 0.0 | 5.0 | 9.0 |
TirgoItem | 4.0 | 1.0 | 2.0 | 2.0 | 11.0 | 10.0 |
SinItem | 7.0 | 2.0 | 5.0 | 0.0 | 17.0 | 11.0 |
CosItem | 7.0 | 2.0 | 5.0 | 0.0 | 17.0 | 11.0 |
Item | 7.0 | 0.0 | 8.0 | 2.0 | 15.0 | 12.0 |
Parser | 4.0 | 0.0 | 1.0 | 0.0 | 10.0 | 13.0 |
Poly | 2.0 | 0.0 | 1.0 | 0.0 | 17.0 | 25.0 |
PowerItem | 6.0 | 1.0 | 8.0 | 0.0 | 21.0 | 29.0 |
ItemBox | 9.0 | 0.0 | 1.0 | 0.0 | 22.0 | 44.0 |
Total | 170.0 | |||||
Average | 5.083333333333333 | 0.5454545454545454 | 2.8333333333333335 | 0.36363636363636365 | 11.692307692307692 | 14.166666666666666 |
能够看到,因为Poly
和ItemBox
类中集合了过多本不应属于他们的方法,因此致使这两个类的复杂度很高,最终的Bug也是出如今这两个类的协做中
此次的做业看起来改动很大,其实仔细分析能够发现,只是新增了一个嵌套功能,也就是说,若是保证嵌套的内容没有问题,那么整个程序的逻辑和第二次做业基本是一致的,我也是按照这个思路来组织,并根据指导书给出的因子、项、表达式这样的分层结构来实现的设计
基本就是按照指导书给出的三层结构来实现的设计,下面给出UML图
结构很清晰,容我再次痛斥一次我这种肮脏的优化设计,耦合度真的很高,没法测试,致使系统存在大量的不肯定性,正确性没法保证的同时,还没法单独调试改错,实在丑陋
Class | CBO | DIT | LCOM | NOC | RFC | WMC |
---|---|---|---|---|---|---|
ExceptionUtil | 1.0 | 1.0 | 1.0 | 0.0 | 5.0 | 1.0 |
Main | 4.0 | 1.0 | 1.0 | 0.0 | 12.0 | 2.0 |
PowerFactor | 5.0 | 2.0 | 2.0 | 0.0 | 18.0 | 9.0 |
NumFactor | 7.0 | 2.0 | 2.0 | 0.0 | 17.0 | 9.0 |
Factor | 7.0 | 1.0 | 4.0 | 5.0 | 20.0 | 12.0 |
FactorFactory | 7.0 | 1.0 | 1.0 | 0.0 | 9.0 | 13.0 |
ExprFactor | 5.0 | 2.0 | 4.0 | 0.0 | 22.0 | 15.0 |
CosFactor | 6.0 | 2.0 | 1.0 | 0.0 | 36.0 | 19.0 |
SinFactor | 6.0 | 2.0 | 1.0 | 0.0 | 35.0 | 19.0 |
Term | 13.0 | 1.0 | 1.0 | 0.0 | 51.0 | 49.0 |
Expr | 3.0 | 1.0 | 3.0 | 0.0 | 61.0 | 63.0 |
Total | 211.0 | |||||
Average | 5.818181818181818 | 1.4545454545454546 | 1.9090909090909092 | 0.45454545454545453 | 26.0 | 19.181818181818183 |
能够看出,底层的模块的复杂度还较为良好,而上层的模块,也就是项和表达式,因为过多的化简逻辑掺杂其中,致使这两个类的复杂度很高,是十分糟糕的状况
笔者在第二次和第三次都出现了Bug,并且状况很相似,都是一个几行代码就能修复可是却很致命的Bug,我以为缘由有下面几点:
因此我以为,在设计一个程序的时候,首先,应该保证良好的模块间解耦,从而可以让思惟集中而不用去一直全局考虑,其次,就是要合理组织数据,若是要完成的两件事情有各自的较优数据组织方式,那么从新组织数据也不失为一种方法
关于如何发现别人的Bug,我从两个角度说吧
出于行业内的代码审查和代码走查方式:
这种方式强调看代码逻辑,也就是白盒测试,在看代码的同时不断提出疑问,而后从这些疑问中寻找可能存在的bug,黑盒测试在这里只起辅助验证做用。
然而,在现有的游戏模式下,首先,你要面对的是六七份代码,每份的代码量都在千行左右,并且有的代码的可读性又比较差,毕竟没有统一的设计规范,这种方法基本不可行,毕竟这种方法在企业中的效率也就只是几我的两个半小时几百行代码的样子
因此,萌生了一种很没趣的方式——评测机玩法
我也叫它摇奖玩法,就是经过本身定义一个生成逻辑,而后模拟评测机去一直测测测,何时测到了就算捞到了,这个方法主要的效果是会给编程人员较大的压力和动力去完善本身的代码,对于评测人员,应该能够提升编写脚本的能力,可谓收获颇丰呢
既然说到设计模式了,就简单提一下
对于本次做业,我以为有下面这两种模式都是比较推荐的:
FactorFactory