LAB4

目录

 

1 实验目标概述··· 1

2 实验环境配置··· 1

3 实验过程··· 1

3.1 Error and Exception Handling· 2

3.2 Assertion and Defensive Programming· 2

3.2.1 checkRep()检查invariants· 2

3.2.2 Assertion保障pre-/post-condition· 2

3.3 Logging· 2

3.3.1 写日志··· 2

3.3.2 日志查询··· 2

3.4 Testing for Robustness and Correctness· 2

3.4.1 Testing strategy· 2

3.4.2 测试用例设计··· 2

3.4.3 测试运行结果与EclEmma覆盖度报告··· 2

3.5 SpotBugs tool 2

3.6 Debugging· 3

3.6.1 理解待调试程序的代码思想··· 3

3.6.2 发现并定位错误的过程··· 3

3.6.3 如何修正错误··· 3

3.6.4 结果··· 3

4 实验进度记录··· 3

5 实验过程中遇到的困难与解决途径··· 3

6 实验过程中收获的经验、教训、感想··· 4

6.1 实验过程中收获的经验和教训··· 4

6.2 针对以下方面的感受··· 4

 

 

 

1 实验目标概述

本次实验重点训练学生面向健壮性和正确性的编程技能,利用错误和异常处理、断言与防御式编程技术、日志/断点等调试技术、黑盒测试编程技术,使程序可在不同的健壮性/正确性需求下能恰当的处理各种例外与错误情况,在出错后可优雅的退出或继续执行,发现错误之后可有效的定位错误并做出修改。

实验针对Lab 3中写好的ADT代码和基于该ADT的三个应用的代码,使用以下技术进行改造,提高其健壮性和正确性:

⚫ 错误处理

⚫ 异常处理

⚫ 防御式编程

⚫ 日志

⚫ 调试技术

⚫ 黑盒测试及代码覆盖度

 

2 实验环境配置

在这里给出你的GitHub Lab4仓库的URL地址(Lab4-学号)。

https://github.com/ComputerScienceHIT/Lab4-1170400307

3 实验过程

请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。

3.1 Error and Exception Handling

本实验中采用自定义Exception类型,每个APP5个,共计15个。

主要涉及到语法错误、无法匹配错误和输入文件信息错误。

 

捕捉到异常之后,我们调用handle()函数处理异常。程序就会在终端打印自定义的异常信息,同时让用户选择输入新的文件,

3.2 Assertion and Defensive Programming

3.2.1 checkRep()检查invariants

针对题目中涉及的每个ADT根据题目要求设计checkRep维护系统不变性,包括Track,CommonObject,ConcreteCircularOrbit以及继承他的子类等。

对于这些ADT,RI、AF以及Rep Explosure已经声明在类中,checkRep根据RI设计。

如果不满足ADT的循环不变性则报错。

 

 

3.2.2 Assertion保障pre-/post-condition

assertion的作用的是确保一个方法的前置条件以及后置条件均满足要求,所以我在有必要进行验证的地方用assertion进行修改。

如图所示,检查前置条件和后置条件。

 

3.3 Logging

3.3.1 写日志

配置文件如下图所示

 

可以看到,在本配置文件中,日志同时被打印到控制台和日志文件,对于DEBUG级别及以上的异常输出到debug.log,对于ERROR级别及以上的日志输出到error.log。其中ConversionPattern决定日志打印格式,日志文件采用了比较容易识别的格式,这里也方便了下一步的日志查询。

1.    在所有可能抛出错误的类中声明一个Logger,对于每一个可能抛出的错误,在抛错的同时使用logger.error打印ERROR级别日志。异常错误的发生时间、类名、方法名、具体信息都能够在日志文件获得。因为在本实验中针对异常错误的处理方式相同(弹窗显示错误信息提示重新处理)所以异常处理结果不计入日志。

2.    在concretecircularOrbit以及其子类中为每一种轨道操作使用logger.info打印INFO级别日志。

3.3.2 日志查询

运用到正则表达式如下:

 

调用方法如下:

 

日志查询的基本思想如下:首先设计一个日志处理配LogParser,这个类提供读取日志文件以及筛选日志的功能。

读入文件时,因为log4j针对不同日的日志分别产生后缀名不同的日志文件,再读入的时候需要将这些日志全部读入,对于每一条日志,使用正则表达式获得日志基本信息(与日志文件输出格式匹配),设置类LogItem对日志基本信息进行存储,存放到LogParser的一个List中。

筛选日志时,根据提供的可用条件在List中筛选满足条件的所有日志,并整合成易于阅读的格式打印。

3.4 Testing for Robustness and Correctness

3.4.1 Testing strategy

我们主要测试的是concretecircularOrbit这个抽象类,但是由于对该抽象类难以直接测试,所以我们把它放在这三个APP中分别进行测试。

因为时间原因,仅完成第二个和第三个,但是这两个完成之后,代码覆盖率已经很高了。

最后代码覆盖率没有达到100%,主要是以下几个原因:

  1. Object和Track,以及其他的一些非功能类没有专门测试;
  2. 中间的一些辅助方法,如菜单文件等没有进行测试,这些测试也是不必要的;
  3. GUI没有进行测试。

3.4.2 测试用例设计

 

3.4.3 测试运行结果与EclEmma覆盖度报告

 

 

3.5 SpotBugs tool

主要改正的错误如下:

1、字符串比较用了等号。

 

2、 读文件错误

 

错误提示说不能直接将参数传给api。

 

3.在删除之前判断是否含有key in map

 

4.在map的子容器类用size()操作等同于直接用map的size()操作。

 

 

改正之后还剩下2个错误:

 

剩下的为:GUI错误,暂时不需要修改。

3.6 Debugging

3.6.1 理解待调试程序的代码思想

FindMedianSortedArrays:查找两个有序数组的中位数。示例中都是升序,

RemoveComments: 消掉给定字符串序列中的注释部分。

TopVotedCandidate:计算t时刻选举成功的候选人

3.6.2 发现并定位错误的过程

(1)FindMedianSortedArrays:

运行有结果但是不正确,进入debug发现看不懂,

 

发现此处之前判断两个数组的奇偶性的时候出现偏差,改过之后发现还是不对,但是对于总个数为偶数个数的测试用例总是通过,调试发现找到的中间数是正确的,只是奇数的时候返回的值有误。

 

改过之后通过。

 

(2)RemoveComments:

(copy进来发现前边的说明部分都是错误)

直接运行输出发现有一行“//”的输出了,然后就加了一个elseif处理了一下直接将那一行删除掉。

 

而且之前有一个标志变量规则设计错误(小错误,一下就看出来了)

 

(3)TopVotedCandidate:

运行, 进入死循环,退出,进入debug,发现算法看的很懵逼,发现死在了计算第一个二分法处

 

此时还是懵逼,然后查了一下二分法的具体实施,又对比了一下后边,但是由于算法看不懂,所以只能走一步看一步。

最开始发现

 

此处看不大懂,不知道c是什么意思,初始是getOrDefault(p,1),运行的时候发现所有的投票记录都在A【1】中,后来反应过来应该是代表个人接受投票个数

 

改过来之后如上,拿题中的数据测试A的第一层是票数,所以看到0票是没有赋值,然后每一次的投票都被记录进list的list里,由于示例中有两个人(0,1)所以第二层有两个。

知道这一点之后看q的方法,可以知道第一层遍历票数,找到离给定的时间最近的投票,然后定位到那一层(由于按时间遍历,最先录入的就是获得相同票的人中最早的人),开始查找时间上满足的,同等票中离时间最近的人。

 

3.6.3 如何修正错误

  1. FindMedianSoretedArrays

 

1)将halfLen=(m+n)/2改成halfLen=(m+n+1)/2

2)将i=(iMin+iMax+1)/2 改成(iMin+iMax)/2

3)将(m+n)%2==1改成(m+n)%2==1

 

  1. RemoveComments

1)     当遇到单行注释的时候,只需要跳过改行即可。

2)     对于/*情况添加 i++跳过

 

  1. TopVotedCondidate

1)将count.getOrDefault改成count.getOrDefault+1

2)将lo=mi改成lo=mi+1

3)I=lo改成i=lo-1

4)将<改成<=t

5)将j = Math.max(lo,0)改成j=Math.max(lo-1,0)

3.6.4 结果

 

 

 

 

1 实验进度记录

请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。

日期

时间段

计划任务

实际完成情况

5/14

18:00-21:55

3.1

完成

5/15

14:00-16:47

3.3

完成

5/15

16:47-22:36

3.3 3.5

完成

5/16

16:00-19:00

3.4

完成

5/17

16:00-22:00

写报告

完成

2 实验过程中遇到的困难与解决途径

遇到的难点

解决途径

Log4j

网上学习

Test过多

选择性写

3 实验过程中收获的经验、教训、感想

3.1 实验过程中收获的经验和教训

LAB3写的不好,LAB4写的很费劲。

3.2 针对以下方面的感受

(1)   健壮性和正确性,二者对编程中程序员的思路有什么不同的影响?

健壮性使程序员考虑更多如边界条件等,正确性可能更多关注程序是否正确。

(2)   为了应对1%可能出现的错误或异常,需要增加很多行的代码,这是否划算?(考虑这个反例:民航飞机上为何不安装降落伞?)

要具体问题具体分析。

(3)   “让自己的程序能应对更多的异常情况”和“让客户端/程序的用户承担确保正确性的职责”,二者有什么差异?你在哪些编程场景下会考虑遵循前者、在哪些场景下考虑遵循后者?

保守与激进的编程方法。在外部接口处倾向于第一种,如果是内部接口的话倾向于第一种。

(4)   过分谨慎的“防御”(excessively defensive)真的有必要吗?如果你在完成Lab5的时候发现Lab5追求的是I/O大文件时的性能(时间/空间),你是否会回过头来修改你在Lab3和本实验里所做的各类defensive措施?如何在二者之间取得平衡?

如果是想要将自己的ADT公共使用的话很有必要。

(5)   通过调试发现并定位错误,你自己的编程经历中有总结出一些有效的方法吗?请分享之。Assertion和log技术是否会帮助你更有效的定位错误?

首先通过试错获得一个错误的输入用例,然后进入debug在关键位置设置断点确定错误区间,进一步确定错误语句。会。

(6)   怎么才是“充分的测试”?代码覆盖度100%是否就意味着100%充分的测试?

测试了边界条件和一些使用方法即可,未必需要100%。

(7)   Debug一个错误的程序,有乐趣吗?

有,但需要足够的时间。

(8)   关于本实验的工作量、难度、deadline。

不做评价,因为自己太菜了。

(9)   到目前为止你对《软件构造》课程的评价和建议。

很难,也学的不好。

(10) 期末考试临近,你对占成绩60%的闭卷考试有什么期望或建议?//请严肃的提出,杜绝开玩笑,教师会认真考虑你们的建议。

希望老师按照同学们实际的学习情况出题,并且重视思想的学习而不是代码的书写,因为在考场上手写代码真的压力很大。

转载于:https://www.cnblogs.com/richardodliu/p/11061667.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值