软件构造实验四

前言:软件构造Lab4

1.1 Error and Exception Handling

为特殊情况定义异常,用异常名就可相对区分所发生的特殊情况,并在提示信息中进行标识,如语法错误:异常名为
在这里插入图片描述
提示信息有标识: 在这里插入图片描述

1.1.1 处理输入文本中的三类错误

为每一类错误都定义对应的异常类型以及各自的子异常:
①不符合语法规则:UnGrammaticalWordException
10种子异常:
机龄大小超出范围:AgeOutofBoundException
飞机座位数超出范围:SeatsSizeOutofBoundException
航班日期存在格式错误:DateFormatException
航班名称存在格式错误:FlightNameFormatException
机场名称出现非法字符:IllegalCharacterForAirportNameException
航班信息不完整(比如有Flight部分信息但缺失plane信息):IncompleteFlightInformationException
机龄、座位数等出现非数字字符:NonNumberException
飞机的编号存在格式错误:PlaneIDFormatException
机型中出现非法字符:TypeContainsOtherSymbolException
②输入文件中存在标签完全一样的元素:SameLabelException
③输入文件中各元素之间的依赖关系不正确:IncorrectElementDependencyException
4种子异常:
两个飞机ID相同,其他地方有不同:IllegalPlaneContentException
起飞日期和降落日期差距大于1天:DateDifferMuchException
航班号相同的航班出发或到达时间/机场不一致:InconsistentStartOrEndException
航班日期与内部起飞时间日期不一致:InconsistentDateException
正如3.1节总述部分所讲,每种异常以异常名和获得现场信息方法中的固定标识如 来加以区分并进行相应的提示。这些异常情况大都出现在读入文本解析或创建航班时,在这些函数里,我们判断是否出现异常情况,如果出现异常情况,就throw,让client做更好地处理:例如: 读入Flight行时可能遇到两种异常:日期格式错误和航班名称格式错误,判断发生后进行抛出,然后在app端捕获并提示相关信息,提示重新读取,这样完成异常处理。 此外,这里的部分异常不仅是只适用于航班创建,同样适用于其他计划项,如创建时存在标签完全一样的元素:SameLabelException,因此在FlightSchedule和CourseSchedule中也加入这些异常处理,如课程创建不允许重名:

1.1.2 处理客户端操作时产生的异常

共考虑了8种异常情况:
位置占用冲突:LocationConflictException
资源分配冲突:ResourceConflictException
进行操作(如删除、分配)的位置还未创建:LocationNotFoundException
进行操作(如删除、分配)的资源还未创建:ResourceNotFoundException
进行操作(如分配、启动)的计划项还未创建:PlanEntryNotCreateException
进行操作(如删除)的位置被占用:PlanEntryOccupyLocationException
进行操作(如删除、分配)的资源被占用:PlanEntryOccupyResourceException
进行状态转换的计划项状态不匹配:PlanEntryStateNotMatchException
与3.1.1节异常处理手段类似,以异常名和获得现场信息方法中的固定标识来加以区分并进行相应的提示,判断出异常情况后进行抛出,上一级进行捕获处理、打印信息进行提示。

1.2 Assertion and Defensive Programming

1.2.1 checkRep()检查rep invariants

①Board类:待展示的所有计划项都应该已经分配资源例如机场信息板:checkRep:
在这里插入图片描述
②每个维度的计划项,如位置、时间:这里以多位置维度为例,多位置就要求位置数>=2,并且各个位置互不相同:

	assert locations.size()>=2;
	Set<Location> locationset=new HashSet<Location>(locations);
	assert locationset.size()==locations.size();//互不相同

其他维度与之类似,采用断言保证不变量
③对计划项、位置、资源进行统一维护的schedule:不变量要求管理的计划项、位置、资源都不应含有重复元素,因此同样用断言保证,如FlightSchedule中:

	Set<Location> locationSet=new HashSet<Location>(locations);
	Set<Plane> planeSet=new HashSet<Plane>(planes);		
	Set<FlightEntry<Plane>> flightEntriesSet=new HashSet<>(flights);		
	assert locationSet.size()==locations.size();		
	assert planeSet.size()==planes.size();		
	assert flightEntriesSet.size()==flights.size();

TrainSchedule中因为为高铁站点额外维护了一个站点名称的String类型的list,因此不变量应要求实际位置的list与其顺序一致,用断言加以保证
④时间对Timeslot:
不变量:形式为 yyyy-MM-dd HH:mm,且结束时间应晚于开始时间
构造函数中用正则表达式保证形式,且没有提供变值器从而保证形式。
在checkRep中转为Date形式进行比较来保证时间上先后顺序:
在这里插入图片描述

1.2.2 Assertion/异常机制来保障pre-/post-condition

如创建高铁车次方法:
在这里插入图片描述
前置条件不允许重名,站点数与时间数匹配,首先对参数进行前置条件检查:
在这里插入图片描述
这里没有引入异常,直接提示调用者
对于重名的前置条件检查:抛出异常
在这里插入图片描述
对于Waiting状态的后置条件检查:使用断言
在这里插入图片描述

1.2.3 你的代码的防御式策略概述

①防御式拷贝,对于可变类的对象属性,观察器和变值器都是生成一个副本,如:
在这里插入图片描述
②利用断言进行不变量检查,如:
在这里插入图片描述
构造器、变值器中对不变量检查,如:
在这里插入图片描述
③用断言/异常保证前置/后置条件:在3.2.2节叙述
④App中对各种非法输入进行防御,如:
在这里插入图片描述

1.3 Logging

使用Java 的库 java.util.logging增加日志功能,将日志写入文件中。
1.3.1 异常处理的日志功能
①异常:之前提到在每个Schedule中都将异常抛出,在app中捕获,因此对app进行日志记录:当捕获异常时进行日志记录
每个app中维护一个Logger变量,名称为类名+Log,如

private static Logger myLogger=Logger.getLogger("FlightScheduleAppLog");

Main函数中为其设置级别以及handler,这里采用文件记录
在这里插入图片描述
每当捕获一个异常时进行日志记录,这里记录的格式由MyFormatter指定,其格式会在3.3.3节日志查询中介绍。
对于异常记录的级别:如果是危害很大,应该终止的异常,记录时设置为SEVERE级别,比如:空输入,文件不存在,时间转化时出错等等;
如果是用户对于计划项、位置等选择型错误,如删除的还未创建,分配的资源导致冲突等等,设置为WARNING级别;
②应用内部断言处理的日志记录
应用内部同样维护一个Logger变量,由于checkRep中调用断言进行处理,因此在checkRep中同时进行日志记录,如:
在这里插入图片描述
值得一提的是在测试过程中对于handler.close的使用,一开始没有handler.close时会导致各个类的对象在互相调用时都会执行checkRep从而使用日志文件,但只要有一个先行占用,就会导致其他类的对象在调用时出现锁定错误:"java.io.IOException: Couldn’t get lock for xxx”,经过查找资料,添加handler.close就可避免这一异常情况。(myLogger同理)

1.3.2 应用层操作的日志功能

App中使用3.3.1中的myLogger,用户每选择一个功能就进行日志记录,即记录用户操作,记录的格式可统一为3.3.1中提到的myFormatter,在3.3.3中介绍记录时设置日志级别为INFO,以与异常相区分,是正常的信息记录。

1.3.3 日志查询功能

为了便于查询,在日志记录时将异常和正常操作类型记录的格式统一起来,定义在MyFormatter类中,继承Formatter类:
在这里插入图片描述
其代表的信息为<时间> <类名称> <方法名称> <日志级别>: <具体信息> \n
前四项自然可以统一起来,至于最后一项,如果是异常,保存的就是异常的提示信息,如果是正常操作,就可以由我们自己定义传进去的信息。
我这里提供三种查询方式:
<1>按时间查询:
app中维护一个时间界限(小时),设time表示当前时间,则用户按时间查询时就是查询time-timebound至当前时间以内的所有日志这时我们只需要遍历日志记录,根据<时间>进行过滤,满足条件的进行显示即可
在这里插入图片描述
<2>按操作类型查询
用户指定要查询的操作,即输入功能菜单上对应的编号,然后进行遍历查询: 用switch-case处理,根据日志级别为INFO以及记录的信息进行过滤
在这里插入图片描述
<3>查询所有异常:
根据日志级别过滤、显示即可

1.4 Testing for Robustness and Correctness

1.4.1 Testing strategy

使用等价类和边界值的测试思想,为各 ADT 添加 testing strategy因为每个ADT的不同方法等价类和边界值可能有较大差别,因此我将测试策略细化到每个方法中,如删除教师deleteTeacher方法:
测试策略:
在这里插入图片描述
然后对每个维度设置测试用例:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在测试异常抛出时利用@Rule声明错误期望对象,
在这里插入图片描述
在执行预计产生异常的语句前进行期望

(expect):expectedEx.expect(ResourceNotFoundException.class);

1.4.2 测试用例设计
对于输入文本带来的异常,在FlightSchedule原有测试的基础上增加对于自定义异常情况的测试,自行构造文本文件,不失一般性,所有测试文件中至少包含两个航班,如航班第一行指定日期与起飞日期不一致:
在这里插入图片描述
构造文本使得航班第一行日期与内部指定的起飞日期不一致:
在这里插入图片描述
所有的测试结果:
在这里插入图片描述
其他与输入文件无关的异常已经在每个方法等价类划分时予以考虑并测量,如3.4.1中介绍的deleteTeacher中等价类划分时就考虑了异常

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

测试结果:
在这里插入图片描述
分支覆盖度:
在这里插入图片描述
语句覆盖度:
在这里插入图片描述
路径覆盖度:
在这里插入图片描述

1.5 SpotBugs tool

Bug 1:对于map排序实现了一个comparator,然后对于entryset使用list自带的addall方法加入list,使用Collections+实现的comparator进行排序
在这里插入图片描述
而对于entry以及entryset应该避免add以及addall方法的使用,其存在隐患。

修改:排序时使用treemap
在这里插入图片描述
Bug 2:清理资源时,资源在某路径上可能为null
在这里插入图片描述
原因:在try之前声明buffReader=null,而如果try中语句不执行,那么此时buffReader=null未改变,导致错误。
修改:在try之前即赋值

1.6 Debugging

1.6.1 EventManager程序

待调试程序的代码思想:
将区间的每一个整数点进行标记(用Map),查询是累加Map.values()查找最大值,也就是说value增加,意味着事件增多,而value增加即代表对应的value为正值,因此将start对应的value设正,end对应的设负,这样就可以通过累加求最大值保证最大交集数

1.6.2 LowestPrice程序

理解待调试程序的代码思想:
逐个offer进行试探,每次将一个special offer加入“购物车”,更新需求,递归求解。对于offer1,offer2,……对于每个offer,都作为选取顺序中的第一个,递归时又作为第二/三/……个,进行重复试探再次进行验证,则遍历了所有offer的组合,维护整个过程中的最小结果,保证了是这些试探结果中的最小的组合

1.6.3 FlightClient/Flight/Plane程序

理解待调试程序的代码思想:
用随机数随机取一个飞机,和其他的航班进行比较,如果该飞机没有被分配到其他任何航班,可以分配。如果被分配过,看二者是否会产生冲突,如果有冲突,再取另外的飞机进行重复试探,当所有飞机都试探过也没有分配成功时则分配失败

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值