• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
Healwh
博客园    首页    新随笔    联系   管理    订阅  订阅

BUAA_OO_UNIT1

BUAA_OO_UNIT1

第一次作业

(1)基于度量来分析自己的程序结构

由于第一次作业还没有任何面向对象编程的经验和任何认识,因此我只有两个类,一个Mainclass输入输出数据,一个 Polynomial用来存放多项式并且求导。

其中Mainclass只有一个main方法。

Polynomial类有2个private变量,分别是poly和der,均为HashMap,key为指数,value为系数。

2个方法,分别是构造方法 Polynomial和输出字符串的toString。

存储多项式和多项式求导均在构造方法 Polynomial中完成,因此构造方法规模极大,

类图如下

可以看出没有任何继承关系和接口等面向对象的内容。。。

这次作业的几乎全部内容都在 Polynomial类中的构造方法中实现,该构造方法的v(G),Iv(G)模块设计复杂度,ev(G)基本复杂度均超过要求

具体分析如下:

ev(G)基本复杂度较高,是由于构造方法中使用了缺少else的if-else语句,使非结构化程度高。

iv(G)模块设计复杂度高,表示我的代码耦合度极高,可能是因为我只用了一个类并且只在构造方法中完成了题目要求的所有内容,使模块难于隔离、维护和复用。

v(G)模块判定结构复杂程度高,原因是判断语句过多,合理的预防错误所需测试的最少路径条数太多,难以测试和维护。

我思考了一下改进的方法,可以将构造方法中的语句提炼为三个方法:构造方法、正则表达式匹配方法和求导方法,这样可以使我的复杂度下降。

(2)分析自己程序的bug

第一次作业在中测、强测和互测中都没有找到bug。自己测试时写了一个评测机,也没有发现自己的bug。

(3)分析自己发现别人程序bug所采用的策略

在同一个room中,用评测机测试其他人的程序,发现了其中两个人的bug。这两个人的bug都是在化简时考虑到了将正系数项放到首项,减少一个字符的长度,但是由于排序时没有将每一项的符号放到系数中,导致排序后的表达式符号错乱。(很疑惑是怎么过的中测)

###

第二次作业

(1)基于度量来分析自己的程序结构

度量类的属性个数、方法个数、每个方法规模、每个方法的控制分支数目、类 总代码规模

这次作业我一共用了10个类和一个接口。

Constant
属性个数:1
方法个数:3
控制分支数目:

Constant:0

der:0

toString:0

总代码规模:19
Tri
属性个数:2
方法个数:3
控制分支数目:

Tri:2

getDegree:0

getFactor:0

总代码规模:30
Cos和Sin继承自Tri类
属性个数:2
方法个数:3
控制分支数目:

构造方法:1

der:3

toString:3

总代码规模:32
Equal
属性个数:0
方法个数:3
控制分支数目:

eqz:2

eqOne:2

isNum:2

总代码规模:35
Expression
属性个数:1
方法个数:3
控制分支数目:

Expression:8

der:0

toString:0

总代码规模:85
Factory
属性个数:0
方法个数:1
控制分支数目:

getFactor:5

总代码规模:17
Mainclass
属性个数:0
方法个数:2
控制分支数目:

simplify:0

main:0

总代码规模:32
Power
属性个数:1
方法个数:3
控制分支数目:

Power:2

der:4

toString:3

总代码规模:19
Term
属性个数:1
方法个数:3
控制分支数目:

Term:6

der:4

toString:0

总代码规模:98

计算经典的OO度量(可使用工具),分析类的内聚和相互间的耦合情况

 

 

 

耦合程度高的地方主要是在Factory类,Expression类和Term类。具体原因是表达式类和Term类的互相嵌套,存在循环调用的情况。Factory类产生各个因子,各个因子又会在有嵌套的情况出现时调用Factory,循环依赖,时耦合度升高。

类图

因为第一次作业是完全面向过程的,整个项目里只有一个 Polynomial类和一个主函数,无法进行任何的扩展,所以我第二次作业直接重新写了。而且因为我实在是没办法搞清加法应该怎样特殊的处理,并且还存在着表达式因子这种嵌套的形式,以及对实验指导书有很多看不太懂的地方(比如哪些情况是这次作业可以进行的求导方式),我干脆把加法、乘法和链式法则的求导都加了进来(实际上我觉得这样需要考虑的东西更少,因为只要把括号内的东西看作一个整体,再对整体操作就可以了)。

在整体的架构中,我参考最多的是实验指导书的这一段

当然,乘法和加法只分别出现在Term类和Expression类中,嵌套关系普遍存在与带括号的因子中,因此我并没有把这三种函数组合规则使用一个接口。

而对于这次作业,因为每个expression中term的连接都为加,term中factor的连接又都是乘法,所以我只使用了列表来存放数据,而不是采用表达式树的形式(其实是我对于树形结构并不熟悉。。。)。

优点

把可能出现的情况都考虑到了,除了要求的表达式因子,还进行了各种因子嵌套的处理。在写Sin类和Cos类时发现构造方法完全相同,用一个父类Tri,这两个类继承父类。

对于Expression中零的判断和Term中1的判断抽离成新的函数,使得复杂度下降,并且可以将多层括号内仅有一个常数的情况直接判定出来。

每个方法的长度大多在20行左右,除了Expression和Term的构造方法,但也控制在了40行左右,和第一次的一个方法100多行相比,可读性有很大的提高。

缺点

对化简的情况考虑的很少,因为第一次使用面向对象的思想来写代码,而且途中遇到了电脑坏掉这种问题,写的确实很匆忙。

对接口的定义还不太理解,感觉指导书中说的Factor抽象成接口和直接定义一个Factor类在这次作业中的区别并不是很大,因为每个子类继承之后直接重写Factor的代码就可以,并且每个Factor的子类都只有构造方法、求导和转换为字符串三个方法。

每个类的设计考虑

Constant

因为常数类只是一个带符号的整数,因此用BigInterger来表示这个数,求导和同String方法也很自然。

Tri

三角函数类是我在写完Cos和Sin的构造方法后发现构造方法相同后写的,考虑到嵌套的可能,我把三角函数的指数用BinInterger表示,括号内的因子用Factor。

我发现Java中的protected是包内共享的,而且CheckStyle会报错,所以我用private变量并且加了public的get方法。

Cos和Sin

这两个类都继承自Tri类,构造方法用super调用。

求导方法先考虑了特殊情况即系数为零、一和二,然后用链式法则将三角函数求导和括号内的因子求导用乘法连接

Equal

这个类是常数Expression和Term类用来判断的。

Expression中的terms中将所有的数用加法连接,只形成一个数

Term中的factors中将所有的数用乘法连接,也形成一个数

Power

这个类只有一个private变量degree来表示Power的指数

构造方法、求导方法和toString都很显然

Factory

类似于pre2的工厂方法,传入一个串,通过开通的字符判断这个串是个什么因子,然后返回一个Factor对象

Expression

这个类是我花费时间最多的一个类,因为要考虑的点比较多

设计这个类时,我考虑的了很久,从最开始的HashMap嵌套三元组,之后HashMap嵌套HashMap。。。

最后我想到,Expression中只有分割成不同的term,不需要考虑term具体是什么,而且每个term之间都是加法连接起来的,因此只需要把每个term存储起来就可以了,所以我用了ArrayList来存储每一个term。

在Expression的构造方法中,根据我的设计,我首先考虑的是如何把一个表达式分割成一个个term。我遇到的最大的问题就是有的term有括号,有的term没有括号,这个问题让我困惑了很久,从周三开放作业到周五也没有思路,到周五晚上和同学讨论时,我终于想到了只要把分割一个个term的特殊的符号找到,就可以把Expression分割成一个个term了。

之后的求导方法,因为Expression由terms以加法连接,因此用一个for循环,将每个term的导函数求出再用加法连接即可

Term

Term类和Expression类非常类似,不同的是Term类由Factor以乘法连接。

有了Expression类找括号的经验,这个类的构造方法就非常容易了。

求导方法依然是用for循环,对每个factor求导,然后与其他项用乘号连接

(2)分析自己程序的bug

这次作业我的代码在中测、强测和互测中均未发现bug,在我自己测试的过程中,发现过的bug有一下三种:

1

x-x**2+x,很基础的问题,找不到括号的话就会异常,也很容易修改

2

cos(x)的求导忘记写负号。。。

3

多层括号嵌套中的因子为数字,因为最开始我的程序在进行括号的拆分时没有考虑到多个连续的括号,类似((((5)))),因此直接抛出异常。

之后我发现BigInteger的构造方法可以直接将带括号的整数转化为一个BigInteger类的对象,因此在Equal类中增加isNum的方法,解决了这个问题。

(3)分析自己发现别人程序bug所采用的策略

这次作业由于我花费太多时间在写代码上,就没有把评测机写得很好,测出其他人代码的bug提交后发现都不符合格式要求,但是因为时间比较紧,就没有修改评测机,也没有进行有效的hack。

第三次作业

因为第二次作业是完全重写了代码,与第一次没有任何关系,因此我的第二次作业实际上已经实现了第三次作业中除了判断WF的全部功能,我的第三次作业只增加了一个Illegal类来判断字符串是否为WF,并且在常数因子(Constant类)中增加了对常数格式的判断(符号和数字间不能有空格),在三角函数因子(Tri类)和幂函数因子(Power类)中增加了对指数的判断(<=50)。

(1)基于度量来分析自己的程序结构

度量类的属性个数、方法个数、每个方法规模、每个方法的控制分支数目、类 总代码规模

除了增加了一个Illegal类,和在Factory中增加了判断WF的正则表达式字符串,其他类与第二次的代码规模相同

Illegal
属性个数:4
方法个数:1
控制分支数目:

isIllegal:3

总代码规模:31

计算经典的OO度量(可使用工具),分析类的内聚和相互间的耦合情况

 

 

 

因为第三次作业与第二次作业相比只增加了判定是否为WF的Illegal类和Power,Constant,Tri中的方法,因此,代码的内聚和耦合情况也和第二次作业相同,暂时也没有想到这种互相嵌套的结构怎样才能减少相互之间的循环依赖。

类图

 

 

 

优点

在第二次作业的基础上只进行了少量的改动,就完成了第三次作业的课程要求,说明我的第二次作业的可扩展性还比较好,考虑的情况也比较全面

缺点

在代码中仍然存在很多String+=这种循环,性能比较差

对结果的优化还很少,考虑过将求导后的字符串作为输入再次构建表达式,但由于我的求导返回的是String类而不是我构造的Term、Expression类,因此想要优化必须要进行重构,为了保证正确性,在有限时间内我只能放弃这种做法

在强测中出现了一个bug,说明我的Illegal类中判断WF的正则表达式考虑的不全面

每个类的设计考虑

除了新增的Illegal类,其他类都与第二次相同

Illegal类的设计思路来源于第二次作业前两天的错误代码思路。。。当时想的是用正则表达式匹配正确的表达式,但是过于复杂无法实现

而判断WF则相对简单很多,只要把实验指导书中列出的WF情况逐一用正则表达式表示即可

我采用的是从非法字符开始判断WF

在自己的测试中通过出现的错误一步步构造新的非法正则表达式,如果匹配就抛出异常

但最后还是不全面。。。

(2)分析自己程序的bug

这次的作业我在强测中出现了一个bug

是因为自己在测试时没有考虑到括号内为空的情况,考虑的还是不够

(3)分析自己发现别人程序bug所采用的策略

随着作业要求的增多和格式检查的出现,能够hack其他人的机会的确很少,但我在强测结果出了之后用括号内为空的字符测试同一个room中其他人的代码,还是发现了有两个人有错误。

这提醒了我考虑全面的重要性,既可以让自己少扣分,还可以在互测中hack其他人

总结

1.重构经历总结

从第一次作业到第二次作业,我感觉难度差距很大,一是对面向对象的思想不理解,而是实验指导书中描述的很多词语也并不清楚

我在自认为读懂了实验指导书中的作业要求后就开始写代码了,但实际上我理解的和实际要做的事情差距很大,这也让我白白浪费了一天的时间。

而理解了作业的目的之后,对作业的实现也是很大的问题,因为总沉浸在第一次作业的阴影中,因此脑中会不自觉想到第一次作业的HashMap,但我认为第二次作业使用ArrayList更加合适,在纠结中,还是选择了ArrayList。

更为悲惨的是周五晚上在图书馆肝代码时,我电脑的主板被烧坏了,我只能借用同学的电脑,熬夜到凌晨三点多,才把之前写好的代码勉强记下来,而其中构思了一半的最为关键的Expression类,却一点也记不得了。在睡了三个小时之后,我又坐在了电脑前,别人的电脑用起来的确很不舒服,但是ddl却让我不得不继续搞下去,终于在周六的晚上我把代码全部写完并进行了第一次提交,虽然有5个点是错误的,但是由于这一次的代码结构比较清晰,很快就找到了bug并且进行了修复。

2.心得体会

1.要维持良好的心态

电脑坏掉的时候心态已经炸了,担心自己没办法完成这次作业,在借到了电脑后还熬夜得很晚,但是其实不熬夜,在周日应该也能把代码写完。

2.要善用讨论区

在三次作业的过程中,即使我没有在讨论区提问,但是已经存在的讨论也解决了我很多问题,给我提供了大量的帮助:

首先是评测机,给了我如何用python相关库构建评测机

然后是第二次作业的递归下降,即使我现在也没懂递归下降是什么意思,但讨论区对递归下降的讨论让我更加理解了题目的要求,从而写除了代码

3.做好pre很重要

寒假的两次pre,设计了工厂模式、继承、正则表达式等,这些都和第一单元的作业有很大的关联,如果没有寒假pre的训练,第一单元的作业只会让我更加焦头烂额,甚至无法完成

4.面向对象的思路

面向对象的思路有很大的优势,但是不论是面向对象还是面向过程,解决问题才是最根本的需求,在第二次作业的过程中很多同学对进行嵌套的表达式因子提出了是否符合面向对象,我认为在处理问题时可以不用太过死板,毕竟解决问题才是最重要的。

5.要早早考虑可扩展性

从第一次作业到第二次作业,我完全重写了我的代码,过程及其痛苦,花费了从周三到周六晚上的全部时间,而第三次作业我只增加了一小部分代码,只花费了短短几个小时。

可见在最初设计时就保证一定的可扩展性,可以对日后的工作有很大帮助。

posted on 2021-03-27 09:31  Healwh  阅读(144)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3