一、结构度量

1. UML类图
  • 第一次作业

  • 第二次作业

  • 第三次作业

2. 复杂度分析
(1)方法复杂度

​ ev, iv, v这几栏,分别代指基本复杂度(Essential Complexity (ev(G))、模块设计复杂度(Module Design Complexity (iv(G)))、Cyclomatic Complexity (v(G))圈复杂度。

ev(G)基本复杂度是用来衡量程序非结构化程度的,非结构成分降低了程序的质量,增加了代码的维护难度,使程序难于理解。因此,基本复杂度高意味着非结构化程度高,难以模块化和维护。实际上,消除了一个错误有时会引起其他的错误。

Iv(G)模块设计复杂度是用来衡量模块判定结构,即模块和其他模块的调用关系。软件模块设计复杂度高意味模块耦合度高,这将导致模块难于隔离、维护和复用。模块设计复杂度是从模块流程图中移去那些不包含调用子模块的判定和循环结构后得出的圈复杂度,因此模块设计复杂度不能大于圈复杂度,通常是远小于圈复杂度。

​ v(G)是用来衡量一个模块判定结构的复杂程度,数量上表现为独立路径的条数,即合理的预防错误所需测试的最少路径条数,圈复杂度大说明程序代码可能质量低且难于测试和维护,经验表明,程序的可能错误和高的圈复杂度有着很大关系。

  • 第一次作业

  • 第二次作业

Method ev(G) iv(G) v(G)
CalSep.CalSep(String) 1 1 1
CalSep.TermBuild(int,int,Term) 1 2 2
CalSep.cal() 10 4 10
CalSep.constBuild(String,Term) 1 6 6
CalSep.isnum(char) 1 1 2
CalSep.issign(char) 1 1 2
CalSep.paraBuild(String,Term) 1 12 12
FormatError.FormatError() 1 1 1
Main.main(String[]) 1 1 2
Regx.Regx(String) 1 1 1
Regx.getMfollow() 1 1 1
Regx.getMpolyStart() 1 1 1
Regx.getMstart() 1 1 1
Term.Term() 1 1 1
Term.Term(BigInteger,BigInteger,BigInteger,BigInteger) 1 1 1
Term.derivate() 1 1 1
Term.equals(Object) 1 3 3
Term.getCoeff() 1 1 1
Term.getCosDegree() 1 1 1
Term.getNorDegree() 1 1 1
Term.getSinDegree() 1 1 1
Term.print(String,BigInteger) 2 6 7
Term.setCoeff(BigInteger) 1 1 1
Term.setCosDegree(BigInteger) 1 1 1
Term.setNorDegree(BigInteger) 1 1 1
Term.setSinDegree(BigInteger) 1 1 1
Term.toString(boolean) 2 6 7
Terms.add(Term) 1 1 1
Terms.derivate() 1 3 3
Terms.fuse() 1 4 5
Terms.out() 1 3 4
  • 第三次作业

    Method ev(G) iv(G) v(G)
    CheckFormat.checkformat(String) 4 7 10
    CheckFormat.isSign(char) 1 1 2
    CheckFormat.judgeSign(String) 3 4 5
    Class1.Class1(String) 1 1 1
    Class1.derivate(String) 1 2 3
    Class1.getNegate(BigInteger) 1 1 1
    Class1.isNum(char) 1 1 2
    Class1.isSign(char) 1 1 2
    Class1.isSpace(char) 1 1 2
    Class1.parse() 2 10 11
    Class1.parseDegree(String) 3 2 3
    Class1.parseNum(String) 2 1 2
    Class1.printTerms(ArrayList) 1 2 2
    Class1.surBrackets(String) 4 4 7
    Class1.toString() 1 1 1
    Class2.Class2(String) 1 1 1
    Class2.addTerm(String,String) 1 1 1
    Class2.delBrackets(String) 1 2 2
    Class2.deriTerm(String) 7 6 7
    Class2.derivate(String) 2 7 7
    Class2.getNegate(BigInteger) 1 1 1
    Class2.isNum(char) 1 1 2
    Class2.isSign(char) 1 1 2
    Class2.isSpace(char) 1 1 2
    Class2.parse(String) 1 10 10
    Class2.parseContent(String) 5 7 12
    Class2.parseDegree(String) 4 2 4
    Class2.printTerm(String,String,BigInteger) 1 8 13
    Class2.surBrackets(String) 4 4 7
    Class2.toString() 1 1 1
    Main.main(String[]) 1 2 2
    Regx.Regx(String) 1 1 1
(2)类复杂度

对于类,有OCavg和WMC两个项目,分别代表类的方法的平均循环复杂度和总循环复杂度。

  • 第一次类复杂度

  • 第二次类复杂度

  • 第三次类复杂度

3. 依赖度分析
  • 第一次依赖度分析

    dependency

  • 第二次依赖度分析

    dependency

  • 第三次依赖度分析

    dependency

(4)分析与改进
  • 优点:类间的耦合程度较低,做到了高内聚,低耦合
  • 缺点:很多方法的模块化程度还不够、复杂度过高,还可以进一步细化。

参考文章:白盒测试之圈复杂度,以及可以直接降低圈复杂度的10种重构技术

  1. 首先提炼函数,将所有可被组织在一起的代码段都独立出来,并让函数名称解释该函数的用途;
  2. 合并重复的条件片段,在条件式的每个分支上的相同代码搬移到条件式之外。

修改前代码:

修改后代码:

修改前复杂度:

修改后复杂度:

可见类复杂度明显降低。将高复杂度的代码进行适当的拆分、优化,可以大大提高代码整体的质量,减少潜在bug存在。

二、分析自己程序的bug

1. 第一次作业

第一次作业中合并同类项的算法错了,好像是下标i,j写混了.....导致合并时符号错误。

第一,这么简单的循环遍历算法都能搞错,实在是太不熟练了...第二,应用HashMap而不是数组进行储存,形成key to value的index索引思想。

2. 第二次作业

第二次没被hack到。

3. 第三次作业
  1. 0的时候不输出,出现了(())的情况,导致格式错误。
  2. cos^n形式输出错误。因为是直接从sin那里复制过来的,所以一不注意就有各种各样的错误。还是要多注意代码的复用,不能老Ctrl-C Ctrl-V

三、分析自己发现别人程序bug所采用的策略

我发现别人程序bug主要靠构造特殊测试样例。

因为觉得完全理解别人的程序比较困难,所以没有结合被测程序的代码设计结构来设计测试用例...

第三次因为表达式输出过于复杂,肉眼实在无法判断是否正确,所以写了简单的python程序来判断求导是否正确:

from sympy import *
from sympy.abc import x str = eval(input().replace("^", "**"))
deri = eval(input().replace("^", "**")) print("str: ", str.diff(x).subs({x:100}).evalf())
print("deri: ", deri.subs({x:100}).evalf())

四、Applying Creational Pattern

工厂模式的好处

我们以类Sample为例, 如果我们要创建Sample的实例对象:

Sample sample=new Sample();

可是,实际情况是,通常我们都要在创建sample实例时做点初始化的工作,比如赋值 查询数据库等。

首先,我们想到的是,可以使用Sample的构造函数,这样生成实例就写成:

Sample sample=new Sample(参数);

但是,如果创建sample实例时所做的初始化工作不是像赋值这样简单的事,可能是很长一段代码,如果也写入构造函数中,那你的代码很难看了(就需要Refactor重构)。

为什么说代码很难看,初学者可能没有这种感觉,我们分析如下,初始化工作如果是很长一段代码,说明要做的工作很多,将很多工作装入一个方法中,相当于将很多鸡蛋放在一个篮子里,是很危险的,这也是有悖于Java面向对象的原则,面向对象的封装(Encapsulation)和分派(Delegation)告诉我们,尽量将长的代码分派“切割”成每段,将每段再“封装”起来(减少段和段之间耦合联系性),这样,就会将风险分散,以后如果需要修改,只要更改每段,不会再发生牵一动百的事情。(参考:百度百科 工厂模式

一开始我是把所有的构造都写在了构造函数里,但这样将对象的创建和使用放在一起,非常不利于封装和分派,不符合设计模式的开闭原则(对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码)。

于是我构造工厂类进行重构,将项的构造分离出来:

构造工厂类前:

构造工厂类后:

至于更多创建型模式,如:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式等,目前还没有太深的理解。需要在以后实践中不断重构来加深认识。

四、基本思路与心路历程...

(一)基本思路

1. 第一次作业

​ 第一次处理输入是用的大正则匹配字符串,建立了Term类和Terms类,构造状态机一项一项往后解析。

2. 第二次作业

​ 第二次允许了多项相乘,且增加了三角函数项,无法再用大正则匹配,因此分为表达式开头、项的开头、项的内容三个类别分别匹配, 并用matcher().find()依此向后匹配,若matcher.start()与上一次的matcher.end()不同,则必定为格式错误。结构上同1,只不过Term类中新加了sin、cos的参数。

3. 第三次作业

​ 本来是想按照指导书说的构建表达式树,但想了好几天没想出来怎么实现orz

​ 于是选择了定义处理表达式和处理项的两个类,先用+-将表达式分项,将分项的每一项结果传到处理项的类中,再将项内的嵌套表达式传到处理表达式的类中,相互递归,直到不含嵌套的基本项递归停止。全程都是直接输出,所以没有任何化简,也没什么架构。老师说这一次作业主要就是为了让我们搭建起好的架构,不然等到多线程时肯定会乱成一团。现在对下一次多线程作业充满惶恐...

(二)心路历程

1. 第一次作业

然而中测却一次AC了,然后自己又测了一些样例竟然也没发现任何错误,就没再管它,最终没进互测。但是发现没进互测之后,赶紧回去自己写测试代码来debug,两秒钟就发现错了。orz 我觉得自己知道有bug的情况下,发现bug在哪里并改正的能力还是比较强的,但是构造测试样例对自己程序进行全方位测试的能力真的太欠缺了。看来计组课下AC的虚假希望还是没能给我留下深刻印象....以后一定自己尽最大努力构造测试样例。

(以及bug修复阶段改了两行就改完了强测的所有bug,但是强测得分是否不会再改变了?)

2. 第二次作业

这次没有被debug出来错,但是因为是自己第一次进入互测,所以觉得收益匪浅。

看别人的代码,真的给人感觉差距太大了。很多漂亮的代码中,那些清晰工整的注释,甚至README中不敷衍了事的、对自己代码认真的逻辑分析,对每次bug修改的详细记录,那种对自己代码的规划感,实在令人感到震撼,并感概于自己代码的乱七八糟...学会管理,即使是很基本的对自己工作的管理,实在是十分重要。

虽然没有被hack到,但是没有费尽心思用各种讨论区里的算法进行优化(不禁感叹我和大佬们真的上的是同一门课吗...明明说的是同一种语言却不太知道他们在讲什么。desperate...)。自己写代码能力和算法熟悉水平都太低了....以后不能再什么都懒得看了,只会和大家越差越多的。

3. 第三次作业

第三次作业差点无效,靠ddl延时才苟过一次orz

我努力思索指导书中的各项提示:表达式树怎么构建、对每种运算方法设一个类的意义何在……思考了一个周末还是一头雾水,丝毫不知道从何下手。就开始用原始方法硬莽,一点点判断,debug到死,花了整整两天debug,却还是深陷设计细节,看不出意义何在。

周二晚ddl过了之后我还是有一个基本点没过,心情简单,发誓不再浪费时间在乱成一团的代码中找错了。明明知道先去学习更多知识,掌握面向对象的方法,来建立更好的架构,思考更好的顶层设计,再下手去实践,是更加事半功倍的明智做法,可是自己还是以“作业要写不完了”为借口不去看书,浪费所有时间在代码的东补西填上,然后没时间去学新的东西,到头来自己毫无进步,如此形成恶性循环。

然后ddl延长后竟然最终过了中测,但是因为抱着“过了中测就行了”的想法,没再努力构造测试样例了,所以自然有很多bug。但是bug修复的时候我也全部发现,并在5行内改掉了8个bug(而且是非同质的...但是因为都是很小的错所以很好改..)。但是!我开始提交的时候没有只提交源文件,把各种二进制文件什么的也全提交了.....导致根本不知道哪些文件自己就改了应该怎么处理,无数次版本回退,pull再push,还是没有成功只修改源文件,其它文件保留上一次push的情况orz。最终放弃。果然.gitignore也不是形同虚设的,要好好利用才行啊。

所以感觉虽然自己通过了这次作业,但其实并未达到此次作业的要求。必须得在进入多线程之前努力学习并应用继承和接口,争取有深刻的理解...

收获与体会

记得有人在群里说,明明是面向对象的课,我们学到的却是什么?如何写脚本对拍,评测机,自动构造样例,这些和OO无关的事情。

但是我觉得其实这些也都是学习OO得到的附加选项,感谢OO让我认识到idea的强大,让我感受到以前死背某函数语法是多么的毫无意义;让我学习到如何得心应手的运用正则去处理字符串;还有git的学习,以前一直记git的指令忘了又记记了又忘,但现在我已经对git有了比较深刻的理解,让人感叹任务导向性的学习才是真的有效......至于对于面向对象好像却并没有太深刻的理解,这就绝对是我自己的问题了...so话不多说去学习了。

OO第一单元总结与心得体会的更多相关文章

  1. OO第一单元总结

    OO第一单元作业总结 一.前言 开学四周,不知不觉已经做了三次OO作业.事实上,每一次作业对我来说都是很大的挑战,需要花费大量的时间和精力来学习. 虽然学得很艰苦,但最后还是连滚带爬地完成了.(好惨一 ...

  2. 【作业1.0】OO第一单元作业总结

    OO第一单元作业已全部完成,为了使这一单元的作业能够收获更多一点,我回忆起我曾经在计算机组成课设中,经常我们会写一些实验报告,经常以此对实验内容反思总结.在我们开始下一单元的作业之前,我在此对OO第一 ...

  3. OO第一单元总结与反思

    OO第一单元总结与反思 目录 OO第一单元总结与反思 摘要 第一次作业 本次作业UML类图 本次作业度量分析 第二次作业 本次作业的UML类图 本次作业的度量分析 第三次作业 本次作业的UML类图: ...

  4. 2020 OO 第一单元总结 表达式求导

    title: BUAA-OO 第一单元总结 date: 2020-03-19 20:53:41 tags: OO categories: 学习 OO第一单元通过三次递进式的作业让我们实现表达式求导,在 ...

  5. OO 第一单元

    OO第一单元总结 前言 第一单元 OO 作业的主题是求导,从最简单的幂函数求导,到添加三角函数求导,再到最后添加嵌套规则.(对熬夜有了新体验,OO 作业比较适合晚上写,OO 博客也是一样 doge) ...

  6. OO第一单元作业总结——表达式求导

    OO第一单元作业总结 第一次作业 基于度量分析代码结构 基本算法 第一次作业是简单多项式导函数求解,不需要对输入数据的合法性进行判定, 基本思想是用 (coeff, expo)表示二元组 coeff* ...

  7. OO第一单元

    OO第一单元总结 目录 OO第一单元总结 前言 第一次作业 HW1基本思路 UML类图 代码规模 复杂度分析 方法复杂度 分析 类复杂度 分析 优化策略 第二次作业 HW2基本思路 UML类图 代码规 ...

  8. OO第一单元(前四周)作业总结

    OO第一单元(前四周)作业总结 OO第一单元(前四周)作业总结要求(第四次作业) 0.前言 本次博客针对的是本人学习Java的第一阶段的三次作业的作业总结 第一次作业的内容是:7-1 计算税率 (20 ...

  9. OO第一单元作业总结

    oo第一单元的作业是对多项式的求导.下面就是对三次作业分别进行分析. 第一次作业 分析 第一次作业相对来讲比较简单,甚至不用面向对象的思想都能十分轻松的完成(实际上自己就没有使用),包含的内容只有常数 ...

随机推荐

  1. FTP:mget匹配文件名后下载

    需求:从FTP某目录取每日构建的apk下载到本地 难点:文件名中有构建时间,而这个时间不算固定值,因此文件名不固定 解决方案:mget匹配文件名后下载 BAT版本: :: Filename:Proje ...

  2. WPF 之 调用线程必须为 STA,因为许多 UI 组件都需要

    WPF中,代码中准备控制控件内容时,有时会报错:“ 调用线程必须为 STA,因为许多 UI 组件都需要 ”. 如在winform下面,使用多线程时,控件的值读取是可以的,但如果要更改,那么就必须进行一 ...

  3. Ubuntu系统查看显卡型号和NVIDIA驱动版本

    查看GPU型号 lspci | grep -i nvidia 查看NVIDIA驱动版本 sudo dpkg --list | grep nvidia-*

  4. 4.17 小发现(dalao勿点)

    洛谷上: (1)iso::sync_with_stio(0); 虽然可以提高cin的速度; 但是有时会RE或WA(如果是WA一般提示Too shot on line); (2)函数最好写上return ...

  5. IDEA安装ini4idea插件

    参见https://blog.csdn.net/lintianlin/article/details/80050309

  6. There is no getter for property named 'XXX' in 'class java.lang.String'解决方法

    <select id="ProjectHomePage" parameterType="string" resultType="java.uti ...

  7. Eclipse中代码字体背景变红/变黄/变绿

    如图所示:运行之后,突然这样.到底是什么原因导致的呢? : 经过查找资料可知:因为Eclipse中有覆盖代码功能 (绿色表示代码被执行到,红色表示代码没有被执行到,黄色表示代码部分执行到) 怎么解决这 ...

  8. 如何配置.Net Core Centos守护进程配置

    一.安装supervisor 运行命令 yum install supervisor 二.配置supervisor 1.运行命令创建文件夹 mkdir -p /etc/supervisor/conf. ...

  9. 学习 Vim —— Vimtutor 总结笔记

    Lesson 2 2.1-2.3 删除 [dw] 删除从光标开始处至下一词开始前的部分,光标停在下一词的词首. [de] 删除从光标开始处至词尾的部分. [d$] 删除从光标开始处至行末的部分. 2. ...

  10. Centos6.5升级openssh、OpenSSL和wget

    1.OpenSSL 1.1.查看版本 使用如下命令查看版本: openssl version 1.2.安装gcc依赖 yum -y install gcc gcc-c++ 1.3.安装配置 ./con ...