OO第一单元总结

OO第一单元总结

一、作业思路

1.1 第一次作业

1.1.1 需求简述

第一次作业要求我们处理一个包含加、减、乘、乘方以及括号(其中括号的深度至多为 1 层)的单变量表达式,输出恒等变形展开所有括号后的表达式。

1.1.2 架构设计

总的来说第一次作业的要求较为简单,出现在表达式里的因子其实只有常数和幂函数,而常数也可以被看做指数为一的幂函数。从这个角度出发,我们的因子其实就只有幂函数一种,其乘除之后的结果仍是幂函数,也就是因子。而表达式就可以被看做由幂函数组成的项的相加减。因此,我的第一次作业的架构非常简单,总共只有三个类,分别是Parser解析类、Factor因子类、MainClass主程序入口类。这也为我之后的两次作业的架构定下了基调。

  • MainClass类

MainClass类主要用来读取输入,并创建Parser类的对象,然后调用Parser类的方法来进行解析,获得一个FactorArraylist,最终将结果合并同类项之后输出。

  • Factor类

Factor类定义了一个因子的基本结构,共有系数coe和指数exp两个私有属性,有了这两个属性,我们便可以把一个因子的全部信息表示出来。

  • Parser类

Parser类主要负责表达式的解析,是本次作业的重点。我在本次作业中采用了递归下降的方法来进行表达式的解析。首先,我们定义了字符串类型的表达式串expression、一个字符串类型的符号数组symbol、一个FactorArraylist作为类的私有属性,分别代表待处理的初始字符串表达式、表达式中连接各个项的加减号、以及解析后的每一个因子组成的数组。其次,我们定义了getExprgetTermgetFactorgetRealFactor四个方法,分别对expression进行层次化解析。其具体功能如下:

getExpr是最上层的解析模块,负责调用getTermgetFactorgetRealFactor,并在解析完成后返回一个Factor类型的Arraylist

getTerm的作用是把一个表达式按照加号或减号来进行拆分,返回一个字符串类型的数组,代表解析出的所有的项。同时,把连接各个项的加号或减号保存在symbol数组里。

getFactor的作用是把项按照乘号来进行拆分,返回一个字符串类型的数组,代表一个项解析出的所有的因子。

getRealFactor的作用是把解析出的每一个字符串类型的因子转化Factor类型的标准因子,并将其返回。

1.1.3 度量分析与架构反思

UML类图

这三个类之间的关系可以用如下的UML类图来表示:

类复杂度分析

其中:

OCavgAverage opearation complexity(平均操作复杂度)

OCmaxMaximum operation complexity(最大操作复杂度)

WMCWeighted method complexity(加权方法复杂度)

由图可以看到,我的MainClass类的平均操作复杂度以及Factor类的平均操作复杂度和加权方法复杂度较高,这其实体现出了我在解题的过程中更多的还是采用了面向过程的思维,把解析的方法全部放在了Parser类中,才会导致Parser类变得如此臃肿。

这张图还反映出了我的另一个问题,那就是代码之间耦合度太高,类与类之间职责不清晰。这可以从MainClass类的平均操作复杂度看出来,我将表达式的合并同类项操作与最后的表达式输出操作均放在了这个类中,这才导致了这个类变得十分复杂。同时,这也导致了我在中间的运算中无法合并同类项,计算成本升高,而且,这也使得我的类之间的职责划分不够清晰。总而言之还是有很大的问题。

方法复杂度分析

其中:

CogCCognitive complexity(认知复杂度)

ev(G)Essential cyclomatic complexity(基本圈复杂度)

iv(G)Design complexity(设计复杂度)

v(G)cyclonmatic complexity(圈复杂度)

由这张图可以看到,我的Parser类中存在很多复杂的方法,例如刚开始的预处理方法preProcess、还有按加减法解析项的方法getTerm。同时,这些方法的行数相较于其他的方法多了很多,这反映出了这些方法的复杂度很高,也就有了更多的出现bug的可能。

架构反思

不难看出,在第一次的作业中,虽然程序的复杂度并不是很高,而且在性能分方面也做到了很好的优化,但是,这次作业还是反映出了很多缺点,比如,我并没有在真正的使用面向对象的思维来解题,这次作业的很大一部分思路仍采用的是面向过程的思维;而且我的程序中的三个类的耦合度很高,没有做到将每个类的功能高度整合并封装起来,例如,Parser类和Factor类的很多属性与方法都耦合在了一起,而合并同类型的方法和表达式输出的方法由于是最后才完成的,我甚至把它们放在了MainClass类里面,导致我的程序结构看起来十分的混乱。

1.2 第二次作业

1.2.1 需求简述

第二次作业要求我们读入一系列自定义函数的定义以及一个包含简单幂函数、简单三角函数、简单自定义函数调用以及求和函数的表达式,输出恒等变形展开所有括号后的表达式。

1.2.2 架构设计

第二次作业相比于第一次作业多出了自定义函数、求和函数以及三角函数,复杂度和难度相比于第一次作业上升了很多,由于时间等一些因素,我没能很好的设计这次作业的架构,仅仅是在第一次作业的基础上新增了两个类,分别是Sum类和Function类,来分别对求和函数和自定义函数进行处理;然后对Factor类进行了调整,使其支持三角函数。

我的基本思路如下:首先一个基本的项可以看做许多因子相乘,表达式可以看做项的相加减。而如今因子多了三角函数,我们就在Factor类里增加两个Arraylist,分别来储存sincos里面的内容,这里我们考虑到后续的迭代,我们把这两个容器的类型定义为了Arraylist<Arraylist<Factor>>,但是,我并没有来得及从中抽象出更高层次的形容,也就是将内部的Arraylist<Factor>看做单独的表达式类,这也导致了我后续的bug。然后新增两个类用来处理自定义函数和求和函数。最后,再稍稍改进一下Parser类使得我们能够将新增的函数解析出来。

  • 改良的Factor类

Factor类定义了一个因子的基本结构,共有系数coe、指数expsin 数组、cos数组这四个私有属性,其中前两个属性可以用来表示常数和幂函数,后两个属性可以用来表示三角函数因子。有了这四个属性,我们便可以把一个因子的全部信息表示出来。

  • 改良的Parser类

这次的Parser类相较于上次,我们需要增加解析三角函数、求和函数以及自定义函数的功能。因此,我们在getFactor方法中添加了匹配多层括号的功能,以便于识别上述的三种新增的因子;然后,在getRealFactor方法中新增了对上述三种因子的解析方法。

  • Function类

Function类用来对自定义函数进行解析,在本次作业中我采用的仍是最基本的字符串解析的方法:当我接收到一个函数调用时,我把其中的形参替换为调用时实际的参数,然后将替换完成的表达式重新调用Parser类中的解析方法进行解析,并将结果作为返回一个FactorArraylist进行返回。

  • Sum类

Sum类用来对自定义函数进行解析,我采用的方法和Function类中的方法相近,仍是字符串替换的方法,生成其代表的实际表达式,然后重新调用Parser类中的解析方法进行解析,并将结果作为返回一个FactorArraylist进行返回。

1.2.3 度量分析与架构反思

UML类图

这几个类之间的关系可以用如下的UML类图来表示:

类复杂度分析

由于这次的架构没有设计好,而新增的功能又变得更多且更加复杂,导致我将太多的方法都塞在了这五个类中,而且并没有将各个类的职责划分明确,这就使得他们的耦合度很高,且全都变得很复杂。

方法复杂度分析

不难看出,这次复杂度超标的方法相较于上次多了很多,他们大多数分布在Factor类、Parser类以及MainClass类中。这是因为新增了三角函数解析的操作,以及新增了不同的同类项合并的操作,而我又没有将它们完全剥离开,仅仅是在源程序上增添或修改,这就导致了许多方法的复杂度急剧上升。

架构反思

第二次作业的架构是我这三次作业中做的最差的一次,仅仅是对第一次作业进行了打补丁的操作,不仅没有进一步优化,反倒让整个架构变得更加混乱,甚至还出现了一些离奇的bug。因此,这也让我坚定了在第三次作业中调整架构的决心。

1.3 第三次作业

1.3.1 需求简述

第三次作业的要求与第二次作业基本相同,唯一不同的是对自定义函数的调用、求和函数的调用以及三角函数的内容放宽了限制。例如函数调用时传递的参数可以是表达式因子,三角函数的内部也可以是表达式因子甚至是函数调用。这进一步增大了难度。

1.3.2 架构设计

这次的架构设计相较于第二次作业做出了较大的调整,其主要变化是在之前的架构上抽象出了Expression表达式类,然后将本就应当在这一层级完成的任务均集成在了这个类里。另一个新增的类是Tools工具类,我们将一些普适且应用率高的一些方法集成在了这个类里,完成了职责的进一步清晰的划分。下面是详细的介绍

  • Expression类建立主要是为了满足三角函数和自定义函数之中可以包含表达式因子这一需求,我们不难发现,随着需求的增多,表达式这个类型需要在程序多处反复使用,如果不单独抽象出来的话,我们孤立的定义一些针对表达式整体的操作便会使程序变得混乱不堪。Expression类中只有一个私有属性,那就是一个Factor类型的Arraylist。我们在这个基础上进一步定义了一个表达式整体的加减乘法、以及它的toString方法。使得程序的结构更加的清晰。
  • Tools类主要是将一些被反复使用的方法集合起来,成为一个外部的工具箱角色。例如之前定义在Parser类中的匹配多层括号的方法,定义在MainClass类中的合并同类项方法与输出方法。同时,我们新增的表达式化简方法也定义在其中。

1.3.3 度量分析与架构反思

UML类图

这几个类之间的关系可以用如下的UML类图来表示:

类复杂度分析

可以看出,经过架构的调整与进一步的职责划分,我们的程序各方面的复杂度都相比上一次作业有所下降。同时,逻辑也相比上一次作业更加清晰。

方法复杂度分析

这次的作业所定义的方法比上次作业更多,但是,我们可以明显的看到,方法的复杂度相较于上一次作业下降了很多。这带来的是逻辑的清晰与职责划分的明确,同时,也减小了出现bug的可能和debug的难度。

架构反思

这一次的架构相较于前一次作业有了较大的改善,而且,经过各个类之间的明确划分,使得程序的逻辑更加的清晰,也使得我能够把一些复用率较高的代码进行进一步的封装,从而改善程序的性能。这一次的架构调整所带来的最直接的好处是:我明显的发现自己程序运行的速度变快了。例如:由于之前化简与合并操作都是在最后一步,导致中间的计算过程出现了大量同类项,拖慢了计算速度。而当我们将合并同类项操作分离到Tools类中,并将其封装在每一次的表达式解析中,我们的合并同类项操作就可以随时进行,从而改善程序的性能,

但是,这并不是说我的架构就是很优秀的。事实上,这次的架构还远远没有达到我所期待的面向对象的层次,其思路本质上还是一种面向过程的思路,仅仅只是做到了职责的明确罢了。而且程序本身还有很多改进的空间。

二、程序bug分析

2.1 第一次作业

第一次作业由于其要求简单,因此,我在公测与互测中均未出现bug。

2.2 第二次作业

第二次作业中由于架构的不清晰,导致我在三角函数内部储存因子时出现了bug。例如,当出现sin(-1)这种样例时,我首先将sin里的内容当做一个表达式来解析,解析出的结果应当是一个Factor的数组,其内部有0和-1,而由于我的化简与合并同类项是在整个表达式解析完后进行的,这就导致我最终会在sin中将0和-1全输出出来,而且,由于我并未抽象出表达式这个类,而是直接使用的Arraylist的toString方法的修改,使得输出字符串时也出了问题。

归结起来,问题主要出现在Factor类里的sincos属性的类型上以及MainClass类里的mergeConteger方法上,由于架构的不清晰,导致了这两者没有很好的执行其功能。

2.3 第三次作业

第三次作业中,由于架构的调整,离奇的bug也少了很多。但是,由于我没有注意到题目对sum函数的要求,使得我的Sum类中的sumCall函数无法处理sum函数下界大于上界的情形。这也提醒了我要仔细阅读指导书,不可因这种疏忽而失分。

2.4 hack策略

在第一次作业中,由于数据类型并不复杂,我搭建了评测机,并使用随机生成数据的方法,来进行评测。

在后两次的作业中,由于数据类型变得更加复杂,我没能完善评测机。因此,我主要采用了手动构造边界数据的方法来进行评测。例如,构造0**0、超过int和long表示范围的大数这种极端数据,又或者是针对大家所做的关于三角函数的优化来进行hack。

三、架构设计体验

这三次作业使得我对于架构设计的重要性有了深刻的体会,尤其是当我在第二次作业中因为一个差劲的架构吃了大亏的时候,我明白了好的架构对于一个项目的重要性。

在第一次作业向第二次作业迭代的过程中,我抱着一种侥幸的想法,觉得这次的需求对于当前的架构来说也勉强能做到,因此便偷了懒,想通过修修补补来过关。结果便是一个混乱的架构反而增加了我debug的时间,还破坏了我的心态。

第三次作业中对于架构的狠心调整,也使我感受到了快刀斩乱麻的快感,这不仅使得我的程序逻辑变得更加清晰,也让我节省下了更多的debug的时间,减少了我程序的bug。长远的来看,调整架构虽然在短期内可能需要耗费我们的一些精力,但绝对可以让我们在后期避免更多不必要的麻烦。因此,以后决不能轻视架构的重要性。

四、心得与体会

本次作业对我来说,感觉在面向对象的方面,收获没有pre大,这其实反映出了我面对复杂的需求时仍是在用面向过程的固有思路。因此,在接下来的学习中,我需要调整自己的思维模式,进一步学习面向对象的知识,并付诸于实践当中。

更重要的是,这次作业锻炼了我项目设计与迭代的能力,让我认识到了顶层设计和架构设计对于一段程序、一个项目的重要性,今后,我也应当更加注重这一方面,并应用到之后的作业与实践当中。

posted @ 2022-03-25 23:39  Levelower  阅读(53)  评论(1编辑  收藏  举报