OO第一单元总结

<!doctype html>unit1

OO第一单元总结

架构

三次作业采用的架构基本一致,即解析器、存储器以及一些用于辅助的类。解析器采用了词法分析+递归下降分析,存储器按照表达式-项-因子的树形结构进行存储。

第一次作业

解析器

虽然第一次作业功能很简单,但为了追求可扩展性,我经过了长时间的思考以及大量的调查,最终决定采用递归下降分析。 但由于思考架构花费时间过多,没能完全掌握递归下降分析,导致采用了寄存器式的返回方式,被调用方法将结果存储到解析器的对应属性中,调用者读取对应属性。 显而易见, 这种设计方式极其丑陋,遇到错误也很难定位错误位置。

存储器

表达式类采用了两个ArrayList分别存储项与运算符号,项类采用了一个TreeSet存储因子。

项类采用TreeSet的目的为在添加因子的同时即可合并同类型的因子。

表达式类采用ArrayList完全是因为开始编写相关代码时对java的容器类认识不足,使用Set失败后改为ArrayList手动控制。尽管如此,依然实现了在添加项的同时合并同类项。

度量分析

avatar

类的继承关系较为直接,只有具体的因子继承于抽象的因子 avatar

由于方法过多,在此仅放出复杂度较高的几个方法。 首先可以看出,项和表达式的复杂度都较高,这是因为第一次作业没有为优化设计接口,在转字符串时进行了格式化,产生了耦合。 除此之外,项与因子的比较器的ev(G)都较为高。项的原因是比较set内容使用了迭代器进行比较,而因子则是由于不同种类之间的因子比较使用了大量的if。可以考虑为每一种因子设置一个属性,表示因子类的大小,直接进行比较会简单很多。 词法分析器的解析方法的复杂度源于对一个字符串轮流使用不同的正则表达式,没有找到什么特别好的优化方案。 项的添加因子方法也较为复杂,主要是因为添加因子与合并同类因子的耦合,应该将合并同类因子抽象为优化的方法。

avatar

对类复杂度进行分析,可以得出表达式、项以及因子比较器三个类较为复杂。项与表达式的复杂主要源于优化没有抽象,而是与添加操作或toString揉到了一起,而因子比较器则是比较方法有误。

bug分析

  • 本次作业没有被发现bug
  • 针对hack其他人,采用的方式为手动构造+自动构造相结合,手动构造主要考虑了正则表达式爆栈与整型溢出。

问题分析

虽然设计很重要,但不应盲目追求可扩展性。如果设计上复杂度过高,应该及时抽身而出,选择一种较为简单的设计方案,防止时间不够用。

第二次作业

解析器

第一次作业中的寄存器处理方式不适合处理复杂表达式,尤其是为了处理表达式因子需要引入多级栈作为辅助,因此进行了重构,通过修改文法的表达方式,实现了通过返回值返回解析结果以及精确定位异常。

存储器

第二次作业中表达式类采用的容器类没有改变,但引进了比较用于表达式因子的比较,以及一些化简方法。

相比于第一次作业,容器类由TreeSet更改为了TreeMap以便于检测到存在同类因子后迅速查找合并。除此之外,增加了化简的相关方法。

度量分析

avatar;

类图相比于第一次,仅仅是多了几个辅助类,以及幂函数的同级因子表达式因子与三角函数因子。

avatar

由于因子的种类增加,因子比较器的复杂度进一步提高,原因及优化措施同前。

squareMerge方法存在bug,最终并没有采用,这也与其复杂度有关。 各种toString的问题依然存在,甚至由于因子的复杂化,引入了新的处理方式而加剧。

对于simplifyFirstNegative,对首项的特判是没有必要的,可以精简。

avatar

由于表达式、项以及解析器类承载的功能较为复杂,因此总复杂度较高。

因子类型比较器的理由同前,不应使用大量instanceof而应编码。

bug分析

  • 本次作业中由于忘记在解析三角函数时解析相应的空白符导致出错
  • 对于hack其他人,考虑到很多人都引入了化简,很容易在复杂度增加时化简出错,本次作业主要集中于复杂的嵌套。

问题分析

由于表达式因子不存在幂,导致即使内容相同,在项中也需要判断为不同,因此为表达式因子引入了唯一的创建编号。同时在表达式一级,又需要仅仅根据内容比较表达式因子用于合并同类项,因此引入了两种因子比较方式,且这两种比较方式又由于复用代码纠缠到一起,增加了复杂度。

第三次作业

第三次作业相比于第二次作业几乎没有改变,仅仅修改了解析三角函数处的解析x为解析因子以及三角函数因子中表达式因子的递归化简。

度量分析

avatar

由于几乎没有修改,各种类、方法复杂度与第二次一致。

bug分析

  • 第二次作业中引入了x**2化简为x*x,导致第三次中sin(x*x)类出错。
  • 对于hack其他人,依然延续上一次的策略,构建复杂的嵌套表达式。

问题分析

本次作业进行时由于笔记本坏了,只能在手机上使用vim进行修改,因此没有引入新的化简,没有大规模修改,也没有做充分的测试。

值得令人注意的是,有些等价的表达方式,可能在需求改变后不再等价。在本次的情况种,三角函数里的x**2和一般的x**2之间就产生了耦合,bug修复时只能是一起修改。由此更加显现了解耦合的重要性。

总结

  • 做好设计工作

oo第一单元充分显示出了设计的重要性。虽然由于格式问题导致第三次作业没有通过强测,但实际上也是很小的问题,也即第二次作业只需要很小的变化即可满足第三次作业的需求。

  • 注意设计复杂度

不能为设计而迷失方向。如果要追求完美的可扩展性等,必然会产生极其复杂的结构。因此,应该权衡可能的需求与可扩展性,得出较为合理的设计后就可以开始动手写代码。

  • 解耦合

从bug修复的经历中可以看出,高度耦合意味着一处崩处处崩。需要时刻注意模块间的耦合程度,尽量做到解耦合。

 

 

posted @ 2021-03-30 17:30  kirimiko  阅读(80)  评论(0)    收藏  举报