面相对象设计与构造第一单元作业分享

1. 基于度量分析代码结构

1.1. 类与方法代码总揽

在我的三次作业中,我共设计了11个类,没有用代码包。共有23个public方法,平均每个类有2个多一点的方法。

1.2. OO度量与内聚耦合

对于内聚性而言,在parse类中,我将除了parseExpression以外的所有函数都设置为了private,这证明了parse中就是一个相互递归调用的整体。在polynomial类中,也集成了众多的方法,内聚性较高。但是作为孪生的unit类,我在设计的过程中反复的提醒自己,unit作为一个类的意义在哪里。这敦促我有意的将相关性更高的数据和方法放在相关的类里,减少get和set的使用。

从类图可以看到,在我的设计当中几乎没有get和set方法,这算是我在真正践行高内聚低耦合理念的表现吧。

1.3. 类图以及优缺点

第一次作业当中,因为码量没那么大,每一个类还算是均衡发展,但是到了第二次作业,polynomial类和unit类的方法总量大增,并且三个继承polynomial类的expression类、power类、number类、term类只是初始化形式不同,剩下的功能均在polynomial里,polynomial类里的方法有一部分也分散到了unit中。这是我代码的缺点,不知道是不是有点抽象过头了,导致父类的东西很臃肿,子类之间几乎没什么不同点。

所以有没有可能,在以后考虑问题的过程中,可以从原子表达式出发,将原子表达式氛分为系数部分,x次数部分,exp指数部分,分别建类,有各自的求导方法。类内内聚性高,类之间耦合性底,层次结构也十分明确。

我最后的toString方法也十分的臃肿,里面集成了优化的所有方法,这样遇到多层exp指数嵌套的情况就会出现解析速度很快,但是输出结果速度很慢的情况。

优点在于迭代好开发,主要归功于第一次作业奠定的基调是正确的。

2. 架构设计体验

2.1. 第一次作业架构

关于架构设计,我认为最最基础的就是在第一次作业了。在第一次作业当中,面对刚刚发布的指导书的我十分疑惑,不知道该怎么去下手。对整个解析表达式的过程完全摸不着头脑,具体细节也完全不清楚。

在这个时候,课程组发布了training的内容,加上第二天早上的第一次上机,为我第一次作业提供了三种思路。

2.1.1. 思路一:正则表达式

在training1中,仍记得training的内容是将只含+*和数字的表达式按后缀形式输出。总体上由于结构简单,可以利用正则表达式加快解析效率,解析成表达式树。这让我想起了上学期数据结构作业中的表达式计算,利用栈将中缀表达式转换成后缀表达式,最后压栈进行输出。

最后解析获得的结果是一个表达式树,通过遍历这棵树,可以获得表达式的后缀形式。

2.1.2. 思路二:递归下降法和正则表达式结合

在training2中,给出了括号,表达式的形式更加灵活。为了处理这种更加灵活点表达式形式,我初步了解了课程组建议的递归下降法。对于具有严格文法的表达式而言,每一次的解析都是固定的,可能得情况是有限的,所以我们就可以很容易的使用控制流程语句来正确且准确地解析表达式,从而获得准确的结果。

与training1相同,最后的解析结果也是表达式树,转换成后缀表达式输出。

2.1.3. 思路三:涉及变量使用递归下降

在第二天早上的上机考试中,也是解析表达式,但是它给出了全新的一种方法。因为涉及到的变量提前被赋值,所以解析的时候顺手就直接替换了。但是在解析表达式的实现方法上,没有后缀表达式输出的格式了,而是解析的结果本身就是答案。因此,这个结构没有行程表达式树,而是直接解析结果,并且将上一级的结果计算之后得到下一级的结果。如此递归往复,最后得到答案。

这个思路对我有很大的启发,但是被解析的表达式对象本身就不是很复杂,也没有括号,所以在实验中基本上是直接对数的加减,而不是表达式和表达式的加减。

2.1.4. 我的思路:递归下降

面对第一次作业,只有单层括号,这对于使用正则表达式的方法很有吸引力。但是考虑到正则表达式不够灵活,我还是放弃了。由于要考虑括号,所以毋庸置疑,肯定要将思路二纳入考虑范围内。这时候就想了,我到底要不要建立一棵硕大的表达式树?我是否要将所有的代码转化为后缀形式再进行计算?面对那么多连续的正负号,我又该怎么处理?

面对这么多问题,我又无从下手了。这次不是在方法的选择上,而是在一个又一个具体细节的实现上犯了难。没关系,我在接下来的过程中将一个个解答。

不能干想,我得写点什么。于是在正式开始的第一个夜晚,我先仿照training2的架构写好了基本的东西。看到舍友的架构,有点启发。对于expr、term、number,可以建一个公共的父类,polynomial,这个polynomial里面有一个hashmap,key对应着x的指数,value对应着这一项的系数。因此,从本质上来讲,所有从表达式、项、数字、因子,化到最简都是多项式这种结构。这种结构的优点在于化简,只需要寻找到对应x的指数,将系数相加即可。

如此一来,架构就明晰多了,也是我最终迭代作业的基本架构:采用递归下降法解析表达式,解析的结果是经过计算和化简的表达式的值,层层递归,最后得到结果。

更具体的细节,编程实现表达式的相加和相乘,而对于乘方,则检测到指数是多少就循环几次,但是要注意在进行乘法的时候不要改变因数的值,否则结果会出错。对于正负号的处理,我认为每一个表达式都有正负号,被解析的表达式默认为正,之后的每一层一但检测到符号就把符号取反,将外层符号向内传播,最终落实到每一个数字或幂次上面。

2.1.5. 最终结果

经过几天鏖战,终于在周五晚完成,顺利通过强测。

2.2. 第二次作业架构

新增exp因子,新增函数因子,括号可以嵌套。

递归下降天生支持括号嵌套呢。

结合第一次作业,我建立了每一项中从次数到系数的单一映射,但是由于考虑exp,则是exp指数部分的因子和次数对系数的二对一映射。于是我将exp与index放在一起,成为新类unit,实现unit对系数的一对一映射,polynomial中依旧采用hashmap结构。

然而这部分需要对乘除法、加减法进行重写,码量比较大。加法就需要合并同类项,合并的依据就是判等。对于exp指数中表达式的判等稍麻烦,需要逐项递归判等,而且为了避免无限递归,我在unit中专门设置了expValid变量来确定exp指数是否为0。在判等的时候先看是否exp指数有效,再进行下一步判断。

对于检测到函数,我的策略是字符串替换,调用解析方法重新解析,获得结果。

最终强测顺利通过。

2.3. 第三次作业架构

第三次增加求导因子,增加函数定义时调用其他函数。

我第二次作业中字符串替换的方法天生支持函数递归调用。

若是求导,则对表达式的每一项求导,求导结构相似,答案可以按求导公式解析,不用管乘法公式和链式法则。

最终也是,强测顺利通过。

2.4. 新的情景:支持分式

如果说在这个新的情景里面,支持除法分式,会对几个方面产生影响。

  1. 最小因子的一般形式,即unit的格式。unit当中除了x次数,exp指数,会增加一项:分布的表达式因子。分式因子作为与表达式因子、常数因子并列的地位单诞生。
  2. 因此,对于表达式的解析,需要额外检测'/'。
  3. 对于表达式的相加,需要增加分母的判等。
  4. 表达式的相乘,分母也需要相乘。
  5. 对于出现在分母和exp指数里面的分式,原本输出即可。
  6. 对于求导运算,需要新增分式的求导法则,好像复杂度有点高。
  7. 优化方面,可以进行约分的优化,有点难了。

总而言之,若新增分式功能,我的代码可扩展性还能接受,但是polynomial因为是对所有表达式的共性的集合,所以新的方法会暴涨,没有其他类来分散,复杂度会很高。

3. 程序bug

在第二次迭代中,乘法和加法会遇到深浅克隆的问题。那第一次作业中为什么没有遇到呢?这是因为bigInteger与字符串一样,都是静态变量,要修改只能指向新的地址空间,原来的内容不会改变。但是我自己实现的unit中exp表达式的乘法就不是了,所以会出现(exp(x)+1)^2展开错误的情况。

4. 发现别人bug的策略

在胡测的环节当中,我对于别人的代码策略是将其打包成jar,送入评测机种自动进行测试。如果能测出来,那就好好看看代码,如果是正确的,那就放过去吧,不值当再多花功夫了,毕竟胡测只是督促同学们阅读别人优秀架构的手段。

5. 分析优化

第一次作业的优化主要在于同类项的合并,这很容易实现,将对应次数的系数相加即可。在输出的时候,某些项是正的,某些是负的,先输出正的,可以少个符号。

第二次作业的优化可以将exp指数中的最大公因数提出变成指数,这是唯一的优化了。没有拆加号,也不会拆。

6. 心得体会

本单元是面向对象设计与构造课程的第一单元,一上来难度暴涨,压力很大。面向对象的思想在本单元中有较好的实现,对每个因子、表达式、项对象的方法与实现提现了面相对象的思想。其中也听到了继承和接口,使得形式进一步抽象。老师在课上讲过,数据抽象用继承,方法抽象用接口。这在本次作业中也有提现。

7. 未来方向

第一次的作业除了面向对象,还有一部分的面相过程,比如表达式的递归解析过程。课程组提供的教学方式也很好,课上围绕着作业进行,实验与课堂不割裂,学习很通畅。

如果说有一点建议的话就是能够少一点特性,多一点真诚,例如exp的优化。而且到了后期,看到几乎很多人在讨论区发表同质化的内容有点浪费大家的时间,本来可以去做更多有意义的事情。

  • 21
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值