首页 > 其他 > 详细

2021 OO第一单元总结

时间:2021-03-27 22:40:45      阅读:39      评论:0      收藏:0      [点我收藏+]

2021 OO第一单元总结

程序结构分析

designiteJava分析结果

classLOCNOMLCOMFanINFANOUT
MainClass 16 1 -1.0 0 2
CheckFormat 178 4 1.0 1 1
Item 166 8 0.87 6 0
Oadd 50 5 0.0 0 1
Omultiply 78 5 0.0 0 1
Inumber 27 5 0.4 3 0
Ipower 49 5 0.4 0 1
Isin 53 6 0.0 1 3
Icos 53 6 0.33 1 3

类图

技术分享图片

程序结构分析

本次实验的目标是实现多项式求导,根据老师上课的提示,我将所有数据类型与操作符类型都继承自Item类。使处理输入与求导后得到的都是Item类型的对象,以方便嵌套构建对象与递归调用方法。

Item类具备求导,克隆,与转换成字符串等核心方法。是所有数据类与操作符类的父类。我将处理输入的方法也放入Item类中,三个方法getTerm,getFactor,makeFactor分别处理的的是表达式,项,与因子。getTerm负责将多项式拆解为项然后调用getFactor方法处理项,getFactor负责将项拆解为因子然后调用makeFactor创建因子。makeFactor倘若接收到常数因子或者幂因子就直接完成创建并返回,倘若接收到的是表达式因子就需要调用getTerm处理括号内部创建一个新的加法类,倘若接受到的是sin或cos因子就调用makeFactor方法创建一个因子作为sin/cos的类属性。如此递归调用最后从getTerm方法中返回一个Item对象,即完成了输出到对象的过程。由于这个过程本身与Item类没什么联系,将他放入Item中导致Item类的内聚性大大下降。应该新建专门的输入处理类来完成这些操作。

CheckFormat类专门用于检查输入的格式是否正确。由于CheckFormat类没有类属性而只是一类方法的集合,使其缺少内聚性。格式检查采用特判与递归检查两部分。我偏执的想继承前两次作业的捷径——去空白符,所以我在递归检查前先对“有无其他符号”,“有无违规空白符”,“有无过多符号”,“有无不匹配的括号”进行特判,然后去空白符后在扔入checkTerm中使用类似于Item处理输入的方式递归检查格式。然而对违规空白符的不完全覆盖导致我不能处理sin(+ 1)这种错误格式,从而将强测的一个点判错。究其原因还是图省事,没有把空白符加入到递归判断中来,导致bug。

数据类的切分:我将多项式中的所有数据分为“数字”,“幂”,“sin对象”,“cos对象”,“加法对象”,“乘法对象”六类。其中Isin与Icos类型包含一个Item类的属性Factor记录其中保存的因子,有一个Integer的类属性exp保存次方。加法对象与乘法对象中均有一个ArrayList保存连加或者连成的Item。这六类均重写了父类Item的getDer方法与getValue方法。由于所有类的getDer方法的返回值均为Item,所以求导时可以非常方便的递归调用getDer方法。例如一个Oadd类型对象的求导就是对ArrayList中的每一个Item求导,将所得组成一个新的ArrayLIst,然后以此创建新的Oadd类。

缺点:除了上文已经提到的Item类负责处理输入的缺陷之外,我认为本设计存在的最大缺陷在于没有将次方运算单独出来作为一类,而是将次方作为类属性分别存储在Ipower,Isin与Icos中。这导致如果需要扩展接收表达式的次方,例如(x+1)^2时,又需要对对象进行比较大的改动。应该设计一个新对象Power表示任意基底的幂运算,它具备Item作为幂的底与exp作为次方数。同时将Isin,Icos,Ipower中的exp属性撤掉。第一单元作业让我认识到数据类越简洁,设计就越灵活,程序的可拓展性就越好。宁愿采用简单的类与复杂的嵌套,也不能使用复杂的类。过多的属性让类变得僵硬,在需求改变时常常导致重构。

bug分析

第一二次作业由于采用接近面向过程的设计与相对完善的逻辑推理,强测与互测环节并未出现bug,第三次作业匆忙重构,在强测中产生了三个bug。

  • 第一个bug就是之前提到的对于cos(+ 1)这种错误格式的判断错误。由于图方便而采用先特判空白符错误后去空白符检查格式的方式,为空白符的错误埋下伏笔,这种错误也能通过特判避免,但也不能保证这样以后就能无错。因此更好的方式是递归检查时就引入空白符。这样才能从根源上避免这一类问题。

  • 第二个bug是在判断输入数据的次方数绝对值是否大于50时出现的错误。我才用了Integer.parseInt方法将输入的次方字符串转化成Int然后与50比较,这导致我在读入一个非常大的次方时直接Runtime Error 了。将此方法替换成new BigDecimal即可解决。这种错误的产生来自于不严谨与没能考虑周到。

  • 第三个bug是在处理非常多的括号嵌套时超时。由于我的程序采用嵌套递归的方式,每次碰到表达式因子都会递归调用getTerm方法处理表达式中的多项式,导致括号嵌套时出现了计算量很大的缺陷。

发现bug策略

由于没有搭建测评机,我第一单元发现别人bug数目较少,主要通过使用自己在编写代码中的错误经历构造数据进行hack。同时也采用读代码针对性hack的方式。下载评测屋中所有人的代码,拿根据自己之前错误记录构造出的数据进行尝试,然后大致观察别人代码的架构与风格,对看起来比较杂乱的代码“比如采用很多if else ,switch case,进行走查与重点测试。

重构经历总结

三次重构类图:

技术分享图片

技术分享图片

技术分享图片

可以看到,第一次作业由于要求很少,主要精力都击中用于正则表达式的构建,至于架构则只创建了一个具有coe与exp属性的Item类,求导方便,但是可拓展性很差。第二次作业尝试使用面向对象的方法,为每一种数据类型都创建了类,并且求导返回的也是一个Item类。但是由于心系化简,我为Item类增加了一个ArrayList<HashMap<>>数据结构来记录对象中存储的数据。化简容易,但是可拓展性很差,加法和乘法时计算复杂,可拓展性也不好,没有真正运用面向对象的思想。第三次作业保留了第二次作业的类,但舍弃了第二次作业储存数据的数据结构,每一个类只需要记住自己的那部分信息即可。三次重构下来最大的教训就是:优化要在设计结束后在考虑!第二次设计时我已经有了第三次框架的想法,但是由于担心化简难度大,硬生生创建了一个数据结构,导致真个设计框架直接受到巨大的影响,代码非常不优雅。第三次作业我先放弃优化,设计的非常畅快与顺利。设计完后发现其实也是可以优化的(虽然最后因为时间问题没有做什么优化)。所有以后一定要清晰地认识到:先做出来在考虑优化,不要因为优化影响实现的框架。

心得体会

初次接触面向对象思想,经过三周的折磨也感受到面向对象的方便与魅力。其中最让我喜欢的是面向对象的多态特性:每个对象根据自己的类型执行自己方法的特性。有了这一特性,在执行特定任务时就不需要很多繁琐的讨论判别,而是创建许多不一样的对象,然后调用它们各自的方法,调用了什么对象自己知道。还有就是思维方式的变化:学习面向对象后感觉拆分问题的能力显著提升。面对问题考虑的是拆分成几个细小的问题在构建对象去实现它。比起以往用C语言面向过程编写有所变化的是对象由于具备自己的类属性,在处理具体问题时比C语言的函数方便得多。对象的属性由自己维护,并且可以根据自己的属性执行自己的方法,具备更强大的能力。并且,经过多次重构,认识到从问题中抽象提取特征周建类的重要性。合适的类构造可以完美贴合问题的处理逻辑,十分顺畅的完成任务的同时也能具备很好的拓展性。用面向对象写程序感觉更接近于将现实世界中的问题抽象到代码世界中,能真正贴近真是的解决情况。

 

2021 OO第一单元总结

原文:https://www.cnblogs.com/ffgsq/p/14586276.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!