面向对象设计与构造第一单元作业总结

面向对象设计与构造第一单元作业总结

程序结构分析

第一次作业

整体评价

​ 第一次作业较为简单,程序的复杂度不高且规模较小。但程序的编写未考虑后续结构的优化和可拓展性,因此面向对象的特点不突出且程序整体耦合度较高,给后续版本的迭代带来了巨大的困难。

程序架构

​ 整体来说,\(Item\)类实现了幂函数的基本单元,即\(number\times x ^ {index}\)。并由\(isPos\)标记正负。\(MainClass\)则实现了对\(Item\)类的全部控制,耦合度高。

\(Item\)类仅实现了构造函数和求导函数。而\(Main\)函数则实现了字符串的读入,字符串的替换与分割,字符串向\(Item\)类的转化,求导函数和输出函数。

​ 总之,第一次作业面向对象的思想贯彻较少,且可拓展性差,导致了第二次作业的重构。

程序规模

​ 可以看到整体行数较少。

程序复杂度

​ 各个方法实现的复杂程度差距较大。未体现面向对象的思想。

​ 由于构建的类较少,虽然\(Item\)类未实现较多的方法,但是内部仍然有着较高的复杂度。

第二次作业

整体评价

​ 第二次作业使用了较多的面向对象的思想,在实现程序的基本功能上完成效果较好。

​ 但是在优化的过程中,代码量大幅度增加,导致部分函数耦合度较高,且有些函数功能重合,甚至冲突,造成了较多的bug,导致最后的效果并不理想。

​ 最后发现bug的修正需要对整个化简函数进行重构,且第二次作业使用的读入函数不适合第三次作业。因此,第三次作业仍然需要进行重构 。

程序架构

​ 程序实现的基本思路是,首先根据输入的字符串构建对应的Block二叉树,随后实现递归求导,再将二叉树转化为多叉树,最后对多叉树进行各种化简操作,并输出。

​ 程序实现的方式为将所有的运算符和基本运算单元抽象为类,并调用类的方法处理整个运算过程。其中Block是所有表示运算的父类,Operation类,SevOperation和SingleBlock为继承了Block的类,分别表示运算符类(二叉树),运算符类(多叉树)和基本运算单元类。而继承自Operation类的有MultiBlock类和PlusBlock类,继承自SevOperation的有SevPlusBlock类和SevMultiBlock类。最后,直接继承自SingleBlock类的有SingleSin, SingleCos, SingleNumebr 和PowerFunction类。最后,除了继承自Block类的各个类,还有负责总体控制的ControlClass类和程序入口MainClass。

​ 在继承自Block类的各个类中,均实现了求导方法diff(),二叉树转多叉树方法changeType(),化简方法simplify()和输出函数toString()。在ControlClass方法中则统筹对上述方法进行控制和调用。

程序规模

​ 第二次作业实现的行数较多,其中实现基本功能的代码行数较少,为了优化输出长度而增加了大量的代码量,因此总体代码行数较多。

程序复杂度

​ 部分类复杂度较高,且这些类本身实现的化简方法较为复杂,造成对程序这些部分的把控变得较为困难。事实上,最后复杂度较高的类也是出现bug的位置。因此,在第三次作业中尝试着对类的复杂度进行了一定的控制。

第三次作业

整体评价

​ 第三次作业虽然仍在结构和设计上有些许缺陷,但是相对于前两次作业有着一定的进步。虽然在第三次作业之后不会有第四次作业,但是仍然留出了一定的可拓展性和可优化的空间。

程序架构

​ 程序实现的基本思路是,根据输入的字符串用递归下降的方式检验正确性,并构建Block多叉树,然后进行化简,随后实现递归求导,然后对多叉树进行循环化简操作,并输出。

​ 程序实现的方式为将所有的运算符和基本运算单元抽象为类,并调用类的方法处理整个运算过程。其中Block是所有表示运算的父类,PlusBlock,MultiBlock,PowerBlock,SinBlock,CosBlock,BasicXBlock,BasicNumber都直接继承自Block类,并且实现了Block中定义的各种方法和对isPos属性的应用。最后,除了继承自Block类的各个类,还有负责总体控制的ControlClass类和程序入口MainClass,其中ControlClass实现了递归下降的方法嵌套。

程序规模

​ 在程序的规模上,第三次作业略大于第二次作业。由于时间的问题(重构),第三次作业的优化程度并没有特别高,只进行了最基本的,对输出结果有较大影响的优化。此外,由于采用了递归下降的写法,导致ControlClass的代码长度较大。

程序复杂度

​ 虽然尽可能的控制了各个类的复杂度,但是由于各个方面的优化缺乏较好的管理和架构的设计,导致各个类的复杂度仍然较高。

程序bug分析

第二次作业

​ 在第二次作业中,由于代码量相较于第一次作业有了极大的增加,且程序的数据架构有所欠缺,导致复杂程度较大。

​ 在这个背景之下,我在生成和求导方法中对isPos的定义为辅助生成的变量,在化简方法中,则将 isPos作为了运算单元的正负表示属性,导致产生了冲突。

​ bug修复的方式为完全重构了化简函数,将化简函数中对isPos的使用调整为与求导方法中一致。

​ 在经历了这个bug之后,我开始对类和方法中的属性和方法的定义和使用方式变得较为重视。

第三次作业(提交前修复的bug)

​ 虽然这个bug在提交之前已经修复,但还是想在这里提一下。

​ bug产生的方式是在化简函数中,使用了浅拷贝,导致在后续方法的调用过程中,对Block树的部分节点进行了改变。在最后输出时,出现了正负号被莫名修改的问题。

​ 修改方式为将浅拷贝改为了深拷贝。

bug自测/互测方式

黑箱测试

​ 使用程序生成随机的测试样例,命令行调用jar包,并用python自动进行检验。

​ 其中,程序生成的方式为参展题目中提供的表达式进行递归生成。

​ 虽然生成的测试点的针对性较差,但是能够解决最基本的正确性验证问题。

手动构造样例

​ 在bug的测试中,也自己构建了比较有代表性的数据样例。虽然构建的数据样例并没有做到全覆盖,但是能够对所有的类的基本功能做一个较为简单的检验,同时,也注意了特殊数据(极大数据,0等),特殊长度(容易爆栈或者空串)等数据点的构造。

互测hack

​ 在第一次作业中,通过超过long范围的指数达成了有效hack。

​ 在第二次作业中,使用黑箱生成的数据达成了有效hack,成功原因未知。

​ 在第三次作业中,使用黑箱和手动构造样例的方式成功了多次hack,但是样例均为较为特殊的组合,在此列举两个:\(+-(sin(1)**1)\)\(x*-21\)

重构经历总结

第二次作业的重构

​ 第一次作业因为面向过程的方法的使用较多,方法的耦合度高,且未留出需要的拓展空间,所以需要在第二次作业中进行重构。

​ 此次重构主要重写了第一次作业中的数据存储结构,将ArrayList的存储改为了类之间相互引用的二叉树。同时,由于第二次作业相较于第一次作业优化空间有着明显的增加,因此,在修改第一次作业的优化函数的基础上,重写并新增了优化函数。

​ 第二次作业重构仍然有着较大的问题。首先, 重构之前未仔细考虑重构想要实现的数据结构和处理方式,导致第二次作业仍然非常复杂,存在二叉树向多叉树的转换与多叉树缺乏条理的优化函数。其次,这次重构之后产生了bug,bug的内容详见bug部分,出现bug的优化函数类需要全部重构。

​ 总之,第二次作业的重构相较于第一次作业的面向对象的水平有了较大幅度的提高,但仍然有着较大的问题。同时,也决定了本单元的作业中还需要下一次的重构。

第三次作业的重构

​ 如上文所说,第二次作业中的重构仍然有着较大的问题,因此在第三次作业中仍然进行了重构。这次重构有以下几个亮点:

  1. 面向对象的程度进一步提高,将可以放入类中的方法放入类中,尽可能的降低了控制类中的内容。
  2. 优化了整个程序的数据结构,将第二次作业中的二叉树读入,求导转多叉树直接改为了多叉树读入,求导。这样,在保证功能不发生改变的情况下,使程序变得更加简单,不易产生bug。
  3. 提高了程序的可拓展性,更注重于运算符的搭建,而不是基本单元。举例来说,将幂函数和三角函数转化为了运算符,而在第二次作业中,这几个函数作为运算的基本单元出现。

心得体会

​ 在本单元的面相对象作业中,我进行了两次的重构及bug修复。在这个过程中,我投入了较多的时间,但同时也收获良多,其中包括对面向对象思想掌握程度的突飞猛进(虽然还有较多的问题),对较大规模的程序编写的控制能力,对程序bug的测试水平的提高。

​ 在以往的项目中,我可能会直接开始写代码,当遇到问题时一边继续书写,一边解决问题,这样直至整个程序完成。现在,在进行了OO训练之后,我认识到这种方法的弊端。现在我会尝试着构建较为完成的程序数据结构和架构,然后入手程序的实现,同时在实现的过程中注重阶段性的程序测试。

​ 在以后的程序训练之中,我希望继续坚持下去,提升自己面向对象编程能力的同时,增强工程测试能力,性能优化能力等,在基础正确性完成的基础上,能够保证较全面数据的正确性与正确性,同时尽可能的增加程序的性能。

posted @ 2021-03-27 16:57  RE_REG  阅读(109)  评论(1)    收藏  举报