OO第一单元作业总结

OO第一单元作业总结

总体

先说感受吧,经历了OO第一单元的三次作业和两次课上实验,着实令我感受到了曾经高老师提到的巨大工程量,甚至超过了曾经被我当作最魔鬼的计组。

从效果上来说,通过这三次作业我确实收获了许多知识。首先工程能力毋庸置疑的得到了很大的提升,包括第二次课上实验也让我认识到了自己在审题、分析需求方面的粗心与不足。其次,我也从对面向对象编程一窍不通的小白,变成了入门级小菜菜,初步认识到了到底什么是一个类,学会了如何使用继承、抽象类、多态等写法,从度量分析各次作业的结果也可以看出自己还是有进步的。还有,就是体会到了java大部分功能都有已经实现的库函数可以直接用的爽快以及正则表达式的强大。

从写代码经验上来说:千万别在半夜瞎改代码千万别在半夜瞎改代码千万别在半夜瞎改代码重要的事情加粗说三遍,真的无法预料半夜那混沌的大脑会让你在代码里干些啥,让你写完之后以为自己写的很好,以为自己测试了,实际上并没有,然后第二天啥都想不起来了,真是太容易出bug了,半夜有较大改动时最好用记事本记一下。

三次作业分析

OO本单元有三大作业,从普通的多项式求导,到带有幂函数、三角函数的表达式求导,再进化到加上嵌套函数的表达式求导。

第一次作业

结构

第一次作业的结构如下:

project
|--- readme.md
|--- .gitignore
|--- src
    |--- Derivative.java
    |--- Monomial.java
    |--- Polynomial.java

各类功能如下:

提交源代码中共包含三个java文件,分别为:

  • Derivative.java :包含main函数
  • Monomial.java :单项式类
  • Polynomial.java :多项式类

图示如下:

耦合:

分析

我第一次作业的做法是读入整个字符串之后,首先判断是否有不合法的空格,然后去掉空格,再每次读取一个单项判断是否符合格式规范,如果不符合就退出,否则将项保存起来。求导时,多项式的求导逐项调用单项式的求导再相加。当时的我以为这种做法足够面向对象,将单项式和多项式抽象成两个类,然而从实际分析结果也可以看出这个写法并不好,而且本以为这样的抽象足以支持第二次作业,可以远离重构烦恼,结果OO作业难度上升的真的很魔鬼写第二次作业时还是进行了重构。

第二次作业

结构

第二次作业结构如下:

project
|--- readme.md
|--- .gitignore
|--- src
    |--- factor
    	|--- Factor.java
    	|--- PowerFunc.java
    	|--- SinFunc.java
    	|--- CosFunc.java
    |--- work
    	|--- Monomail.java
    	|--- Polynomail.java

代码简介:

提交源代码中共包含:

两个package,分别为:

  • factor :该 package 包含 一个抽象父类 和 三个子类 用于封装因子
  • work :该 package 包含两个类 用于完成多项式求导

六个java文件,分别为:

  • Factor.java : 属于package factor,是三种因子的抽象父类
  • PowerFunc.java:属于package factor,继承 Factor 类,是幂函数因子类
  • SinFunc.java:属于package factor,继承 Factor 类,是简单正弦函数因子类
  • CosFunc.java:属于package factor,继承 Factor 类,是简单余弦函数因子类
  • Monomail.java:属于package work,是单项式类
  • Polynomail.java:属于package work,是多项式类,包含main函数入口

图示分析:

耦合:

分析

做法:第二次作业的输入处理上和第一次作业相似,但项目结构上进行了重构,多项式使用ArrayList存储单项式,单项式使用ArrayList存储因子,再额外使用Biginterger存储单项式的系数,各个因子类共同抽象出一个父类包含一个index指数属性。多项式求导调用单项式求导再相加,单项式求导调用因子求导先相乘再相加。

优化:本次作业包含三角函数,但不包含括号,因此只能做一些比较基础的化简。(A,B为只有系数不同的单项式)

化简包括但不限于:

(A*sin(x)^2+B*cos(x)^2 =A+(B-A)*cos(x)^2 = B+(A-B)*sin(x)^2)

(A*sin(x)^4-A*cos(x)^4 =A*sin(x)^2 +A*cos(x)^2)

(A-B*cos(x)^2 = A-B+B*sin(x)^2)

(A-B*sin(x)^2 = A-B+B*cos(x)^2)

注意并不能保证每次使用这种化简方法能使答案变短,我使用了比较慢的方法,每次化简完之后使用参与化简的项toString比较长度,而且我使用了顺序查找的方法,并不能保证结果最优。然而助教还是挺仁慈的还是说同学中没有狠人写最佳优化?

从度量工具的分析上可以看出,第二次作业虽然难度增加,但我的耦合度等都比第一次要好许多,但也有不合理的地方,可以说略有进步吧。而且第二次作业构思时间较长,也认真学习了别的dalao的思路,使我在完成第三次作业时并没有进行大量重构。

第三次作业

结构

第三次作业结构如下:

project
|--- readme.md
|--- .gitignore
|--- src
    |--- factor
    	|--- Factor.java
    	|--- PowerFunc.java
    	|--- SinFunc.java
    	|--- CosFunc.java
    	|--- PolyFunc.java
    |--- work
    	|--- Monomail.java
    	|--- Polynomail.java
    |--- io
    	|--- Readin.java
    	|--- Writeout.java

代码简介:

源代码中共包含:

两个package,分别为:

  • factor :该 package 包含 一个抽象父类 和 四个子类 用于封装因子
  • work :该 package 包含 两个类 用于完成多项式求导
  • io:该 package 包含 两个类 用于完成输入与输出

九个java文件,分别为:

  • Factor.java : 属于package factor,是四种因子的抽象父类
  • PowerFunc.java:属于package factor,继承 Factor 类,是幂函数因子类
  • SinFunc.java:属于package factor,继承 Factor 类,是正弦函数(含嵌套)因子类
  • CosFunc.java:属于package factor,继承 Factor 类,是余弦函数(含嵌套)因子类
  • PolyFunc.java:属于package factor,继承 Factor 类,是表达式因子类
  • Readin.java:属于package io,是处理输入的类
  • Writeout.java:属于package io, 是处理输出的类
  • Monomail.java:属于package work,是单项式
  • Polynomail.java:属于package work,是多项式类,包含main函数入口

图示分析:

耦合:

分析

做法:第三次作业在仔细考虑之后,发现自己第二次的架构是足以支持第三次的需求的,因此也就没有重构大改,在原有类的基础上增加了poly型因子并将三角函数因子改为嵌套型因子,在老师的反复要求强调下我还把输入和输出抽象成单独的类,其余思路基本不变。值得一提的是,本次作业对于输入的处理也较为麻烦,令我苦恼了好长一阵子,

优化:本次优化的重点在于去掉多余的括号,还有(sin(0) = 0)(cos(0) = 1)这样的化简,追求极致的话应该需要再写一个完整的合并同类项以及类似第二次作业的三角函数的合并。我去掉多余括号的思路是首先当我们拿到一个多项式因子时,我们需要判断这个多项式因子中是否只含一项从而判断括号是否多余,如 (A*(x)) 这样的单项式是可以保存为 (A*x) 这样的形式的,采用类似的思想我们便可以在构造多项式的时候就化简掉许多无用括号。合并同类项类似于第二次作业的化简,我们需要找到两个单项式的公因式,并且写一个单项式除法的方法。至于(sin(0))这种的就交给最后生成字符串后全部replace了即可。

分析:从度量工具的分析可以看出本次作业也有许多不好的地方,而且为了写优化加了许多面向过程式的方法导致现在自己也看不下去自己写的代码,这么丑的代码,得跟互测同屋的同学说声抱歉了。

自己程序的bug

很值得庆幸的一件事就是,在这前三次作业中我并没有被强测测出bug,也没有被互测同屋的同学发现bug,然而我自己在截止第三次作业提交不久后便意识到了自己代码是存在bug的,没有被发现还真是多亏了我虔诚的祈祷啊

首先,第一个bug,在优化时为了方便的去除由于 replace("sin(0)","0")导致的 (0*x) 这种不该存在的东西,我把replace后的字符串又丢给了我的Poly构造函数,然而没想到的是我的Poly构造函数在指数超过10000时是会抛出异常的,如果计算结果中出现了指数超过10000的情况,我在化简时就会抛出异常,然后GG。

解决方法:在化简时捕捉到异常的话不做处理或者将检查合法性与构造函数分离

其次,第二个bug,合并同类项时有一类特殊的因子就是多项式因子,他特殊在不能有指数,这样以来,可能一个单项式的因子中有多个相同的多项式因子,按我的方法暴力进行除法的时候,被除式如果有多个相同的多项式因子((A)*(A)*(A)),并且除式也有一个((A))作为因子的话,被除式就全没了。最令人智熄的是,我在写代码之前是考虑到这种情况的,但是我写的时候就忘了,而且随便一个简单的这种样例就能测出来的bug,我居然没测,第二天起床还信心满满。。所以不要半夜瞎改代码

解决办法:使用remove操作,当除式中某个因子已经作用过被除式之后便将除式中的这个因子删除。

发现别人的bug

吐槽(一些废话):互测的确能通过看别人的代码收获到不少知识,我也学到了许多,这可能也是互测制度的本意,然而当工程量变大,如第三次作业,想要大家真的通过阅读别人的代码找bug,就算代码风格通过checkstyle统一规范,但看着没有注释、没有readme、完全不知道什么思路的代码还是让人头疼,在以后OS难度飙升的情况下还有多少人会花大量的时间去学习阅读别人的代码呢?当然花费时间这事完全看个人,想花就花不想花随机数据也可以测出bug,只是感觉对大多数人来说互测失去了意义?当然也可能是我自己以偏概全了,我也提不出啥更好的办法,所以只是吐槽一下,希望当作废话处理即可,这篇博客也是半夜写的,可能废话比较多

随机测试:使用matlab、python等多种多样的辅助工具检测求导正确性,再利用脚本自动生成数据和自动测试所有人的正确性

特殊测试:将曾经测出自己bug的数据保留下来测其他人,与同学交流讨论真的会有许多意想不到的收获,你的思维盲区说不定是别人很早就考虑到的东西

定向测试:看看代码容易错的部分,这几次作业主要体现在正则表达式部分

Applying Creational Pattern

向同学讨教之后学习到了工厂模式的使用,即使用静态工厂方法创建对象,在单项式类的构造中工厂模式可以让我更加方便的构造出一个因子对象,避免了方法过长的问题。

原文地址:https://www.cnblogs.com/eitbar/p/10604976.html