# BUAA_OO Unit 1

刘祖权-22371221 学生 2024-03-22 13:03:01

一、整体架构

Unit 1 最终程序架构如下

img

(白色类为 HW1 即存在的类,蓝色类为 HW2 时添加,绿色类为 HW3 时添加)

思路总述:

  1. 读取自定义函数,将函数表达式存储在 replacerfunctions
  2. 读取表达式,Replacer将所有的函数展开,并经由Simpilifier化简
  3. Parser对表达式进行解析,找出其中的运算符,将表达式逐渐分解为多个Term的加减,Term又分解为多个因子/Term相乘。直至解析出最基本的因子:常数、幂函数、指数函数。
  4. 对所得的TermPolynomial进行运算,得到最终的表达式

迭代过程

HW1:

  • 预处理:在开始解析前,删去了表达式中的所有空格,并对表达式中多余的+、-进行了处理。(此时的预处理操作在Parser中进行。)
  • 解析方式:设计时采用 Exp_1 提供的思路进行递归下降。解析时先寻找表达式中的运算符,化为Left op Right的形式,再对LeftRight继续进行解析,直至表达式中不再含有运算符。此时表达式为最基本的两种因子之一:常数/幂函数。
  • 实体类设计:HW1 中主要的实体类为Polynomial,其余的AddSubMul都是继承自Polynomial。所有因子都视为简单的Polynomial,不设置单独的类。

HW2:

  • 新增需求:支持指数函数、自定义函数
  • 改动
    • 新增ReplacerFunction用于自定义函数的展开。读取表达式前,将自定义函数存储在 Replacerfunctions 中。解析表达式前,Replacer将所有的自定义函数展开。
    • 新增Term,由最基本的三种因子组成:常数、幂函数、指数函数。Polynomial则由多个Term组成。

HW3

  • 新增需求:支持求导
  • 改动
    • 将预处理的代码提出,单独设置Simplifier,令Parser专门进行解析工作
    • 新增Polynomial的子类Derivation,用于表达式求导

新的迭代情景

假设现新增需求为支持三角函数,只需在Term中新增一个属性:三角函数,并在Derivation中增添对应的求导规则即可。

二、度量分析

代码规模

Source FileTotal LinesSource Code LinesSource Code Lines [%]Comment LinesComment Lines [%]Blank LinesBlank Lines [%]
Add.java151387%00%213%
Derivation.java161488%00%212%
Function.java1019594%00%66%
MainClass.java211990%00%210%
Mul.java191684%00%316%
Parser.java11210291%44%65%
Polynomial.java787090%11%79%
Power.java191684%00%316%
Replacer.java504590%00%510%
Simplifier.java484594%00%36%
Sub.java181689%00%211%
Term.java938288%22%910%
Total:59053390%71%508%

方法复杂度

此处只展示复杂度最高的几个方法。Total与Average则统计了程序中所有方法.

MethodCogCev(G)iv(G)v(G)
operator.Replacer.replace(String)10446
operator.Parser.findAddOrSub(String)13469
expr.Polynomial.equals(Polynomial)15547
expr.Term.toString()1651010
operator.Parser.parse(String)16767
Total15971101125
Average5.132.293.264.03
  • Parser.parse用于解析表达式,流程和讨论情况较多,故复杂度较高。目前已将匹配三种最基本因子的代码提出,单独形成parseFactor方法。剩余部分也可以进行拆分,但懒。
  • Term.toString是将项转化为字符串的方法,由于程序中没有设置更小的单位,故需要在此方法中考虑常数、幂函数、指数函数所有可能的情况,导致复杂度较高。

类复杂度

ClassOCavgOCmaxWMC
MainClass2.0022
expr.Term2.831017
calculater.Add3.0033
calculater.Derivation3.0033
calculater.Mul3.0033
calculater.Power3.0033
calculater.Sub3.0033
expr.Polynomial3.60718
operator.Replacer3.67611
operator.Simplifier4.67614
operator.Function4.75719
operator.Parser5.50722
Total118
Average3.815.009.83

爆红了5个,均值也偏高……主要是不少方法中都有if-else嵌套的情况。

三、bug分析与优化

My bug

  1. HW1 中,因为在Mul中没有删去合并后系数为 0 的项(AddSub则都有),toString中也没有针对系数为0的情况,故输出错误。当时的解决方法是直接在MUl中增补了删去合并后系数为 0 项的代码。
  2. HW2 中,由于指数类型设置为int,不幸爆掉,遂将指数类型改为了long。(为什么不改为BigInteger呢?因为改为long可以直接使用 IDEA 的重构类型,省事。)
  3. HW3 中未发现bug。(说明赌对了,指数类型设置为long就够用

Hack 策略

  1. 将自己调试程序时发现的bug记录下来:有点效果但不多
  2. 针对自己认为的易错点编几个测试点:似乎没有hack成功的
  3. 下载代码并提交给评测机:效果甚微,毕竟大家的程序在提交前早就用评测机测过多次了
  4. 并没有仔细阅读被测程序的代码来设计针对性的测试样例,效率确实偏低

反思与优化

  1. 对于第一个bug,在本周做了优化,将合并同类项、删去系数为0项的代码写入了Polynomial.addTerm中。一定程度上减少了AddSubMul中的代码量。如果从一开始就这样做,也可以避免考虑到AddSub,却遗漏了Mul的情况。
  2. HW3 时,将预处理的代码提出,单独设置Simplifier,令Parser专门进行解析工作。
  3. Parser.parse中将匹配三种最基本因子的代码提出,单独形成parseFactor方法。
  4. 针对if-else嵌套较多的方法,将可以提前return的分支提前return,分支间可以提取的代码进行提取。

四、小结

心得体会

本以为刚开学各课程强度不会太高,可以小摆一阵。不料 OO 上来就上强度,HW1 与HW2 都花费了不少时间。不过好在是又活过一个单元。

在迭代时没有经历大规模的重构,说明以 Exp_1 的设计思路为基础还是很靠谱的。三次作业完成度都较为满意,但写单元总结时却发现很多方法、类的复杂度都较高,所以又花了一定时间进行了优化(优化的过程有一种治愈强迫症的愉悦感)。对于“面向对象”这种编程思想的精髓理解得似乎不太到位,在高内聚、低耦合方面做的并不好。希望在之后的单元中能有所改善。

未来方向

  1. 相较于 HW2,HW3 的任务量有些过于少了,可以进行适当调整,比如把自定义函数挪到 HW3
  2. 关于性能分,建议课程组针对测试样例得到一个长度适当的表达式,以此为基准进行赋分
  3. hack 时即使是简单的样例也总是无法通过检测,但是又不会显示具体违反了哪条原则,体验不太良好,希望可以优化
...全文
56 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

301

社区成员

发帖
与我相关
我的任务
社区描述
2023年北航面向对象设计与构造
学习 高校
社区管理员
  • YannaZhang
  • CajZella
  • C_ecelia
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧