软件构造实验代码可见github:软件构造实验整理
2021年春季学期
计算学部《软件构造》课程
Lab 2实验报告
姓名
学号
班号
电子邮件
手机号码
目录
目录
1 实验目标概述 3
2 实验环境配置 3
3 实验过程 3
3.1 Poetic Walks 3
3.1.1 Get the code and prepare Git repository 3
3.1.2 Problem 1: Test Graph
3.1.3 Problem 2: Implement Graph
3.1.4 Problem 3: Implement generic Graph
3.1.5 Problem 4: Poetic walks 5
3.1.6 使用Eclemma检查测试的代码覆盖度 7
3.1.7 Before you’re done 7
3.2 Re-implement the Social Network in Lab1 7
3.2.1 FriendshipGraph类 8
3.2.2 Person类 8
3.2.3 客户端main() 8
3.2.4 测试用例 9
3.2.5 提交至Git仓库 9
4 实验进度记录 10
5 实验过程中遇到的困难与解决途径 11
6 实验过程中收获的经验、教训、感想 11
6.1 实验过程中收获的经验和教训 11
6.2 针对以下方面的感受 11
1 实验目标概述
本次实验训练抽象数据类型(ADT)的设计、规约、测试,并使用面向对象
编程(OOP)技术实现 ADT。
2 实验环境配置
本次实验使用的IDE是IDEA,环境为JDK8,Junit4,由于IDEA中Junit自带测试代码覆盖度的功能,因此没有使用Eclemma。
https://github.com/ComputerScienceHIT/HIT-Lab2-1190300321/tree/master
3 实验过程
请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1 Poetic Walks
该任务主要分为两部分,第一部分是使用两种实现方法实现一个图,第二部分就是使用设计好的图来实现一个单词插入的功能,主要就是ADT和OOP技术的使用。
3.1.1 Get the code and prepare Git repository
从网站获取代码之后,将代码修改成需要的结构之后使用git管理,并上传代码。
3.1.2 Problem 1: Test Graph
这一部分主要是实现了对于graph的测试,对于每一个测试方法都使用划分等价类的方法,例如,对于set方法的测试划分等价类:按照需要操作的点划分:两点都存在,两点都不存在,两点有一个存在;按照weight划分:weight>0,weight<0,weight=0;按照边存在情况划分:存在,不存在。其余部分关键方法测试方法如下截图:
3.1.3 Problem 2: Implement Graph
这一部分主要使用两种方式实现一个图,以下分别叙述。
3.1.3.1 Implement ConcreteEdgesGraph
这一部分主要以边来实现一个图,首先需要设计一个Edge类,这个类主要的Rep为边的出发节点、终止节点,由于这是一个immutable的类,因此不存在mutator,主要实现的方法都是获取该边的信息的方法,例如getSource等。
实现一个Edge之后就需要使用它来实现一个Graph,graph的Rep如下截图:
由于这个类是接口Graph的一个实现,因此方法的Spec都已经定义好了,主要的工作就是完成每一个方法。例如其中比较复杂的是Set方法,主要功能是增加、修改、删除边的权重,实现的时候采用的是逐个匹配,如果出发节点和终止节点都匹配的话修改其权重,其余情况或是删除边,或是增加边,由于代码过长,不在此展示代码。
3.1.3.2 Implement ConcreteVerticesGraph
这一部分实现的时候和上述用边实现的基本一致。首选需要实现一个Vertex类,这个类是一个mutable的类,因此其存在mutator,在设计的时候,主要的mutator是改变指向当前节点的节点集合与当前节点指向的节点的集合,而其余的方法主要是返回当前节点的一些信息的。
实现了Vertex之后主要的任务就是实现一个Graph,基本的实现方法与用Edge实现的一致。不同之处主要在于在寻找边的时候Edge实现的图直接将每一条边的source和target与目标进行比较,如果相同则找到了需要的边;用顶点方法实现的时候需要遍历每一个节点,在指向当前节点的集合和节点指向的集合里面匹配目标,因此遍历的次数显然较多。但是对于节点的增删较为方便。
3.1.4 Problem 3: Implement generic Graph
3.1.4.1 Make the implementations generic
这一部分主要是将String实现的类改成泛型L,首先可以先把所有的String都改成L,会发现有一定数量的报错,这些报错都是因为在之前实现的时候用了String类特有的一些方法,因此导致了错误。因此需要将这些方法修改成通用的方法,就可以消除报错。
3.1.4.2 Implement Graph.empty()
可以发现这是一个静态工厂方法,在实现的时候只需要将其返回一种图实现方式的一个示例即可,具体代码截图如下:
3.1.5 Problem 4: Poetic walks
这一部分需要实现的就是使用上述实现的图的两种方式中的一种采用OOP技术实现一个在文本的特定位置插入特定字符的任务,主要叙述如下:
3.1.5.1 Test GraphPoet
在设计测试样例的时候首先进行等价类划分:从文本结构来分:空文本、单行文本、多行文本,从是否需要从多个单词中选择一个来看:需要、不需要,因此使用五个txt文件来存储对应的测试样例,并将结果与预先设定的结果比较,观察是否符合。
3.1.5.2 Implement GraphPoet
这一部分第一部分主要是为了读入语料库的文本的,在处理的时候使用一个Map来统计每一个字符对(相邻的两个字符组成一个字符对)的出现次数,同时将读入的语料库作为一个List备用。主要代码部分截图如下:
接着是实现功能的主体部分。主要设计思想是建立一个StringBuilder来存储最后需要返回的文本。每次循环的时候都加入一个原有的文本,并根据原有文本的当前字符与下一个字符遍历比较在语料库中是否存在一个字符能连接两个字符,如果存在的话加入,如果存在多个的话选择在语料库中出现次数最多的那个字符,最终返回一个String即可。需要注意的是除了最后一次插入,每一次插入的时候都需要额外插入一个空格符。由于这一部分代码太长,不在此展示。
3.1.5.3 Graph poetry slam
这一部分主要是为了测试上述的功能是否完善的。因此在代码中采用了《生如夏花》的文本作为语料库进行测试,测试部分结果如截图所示:
3.1.6 使用Eclemma检查测试的代码覆盖度
注:由于采用的IDEA的Junit自带检测代码覆盖度的功能,因此没有采用Eclemma
3.1.7 Before you’re done
请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
使用git add ,git commit, git push等命令上传
在这里给出你的项目的目录结构树状示意图。
3.2 Re-implement the Social Network in Lab1
这一部分主要是重新实现Lab1中的社会网络部分,可以测试上述graph的设计是否完善。
3.2.1 FriendshipGraph类
使用上述的以边建立的图来实现这个类,将每一个节点设置为Person类型即可。
该类具有两个属性与三个方法。首先两个属性分别是People列表与已经存在的人名的集合nameSet,后一个集合主要是为了处理两个人重名的不合法输入情况。在处理完输入不合法情况之后需要对三个方法进行处理。
其中加入朋友关系的方法只需要使用graph的set方法即可。
较为复杂的getDistance方法使用了java中的Map,并使用BFS,在BFS每一次执行中都比对当前的节点是不是所求节点,如果是直接输出距离,如果不是且该节点未访问过,那么使用Map将其与一个当前距离+1联系起来,直到寻找到需要的那个节点或是寻找不到输出-1。getDistance部分代码截图如下:
3.2.2 Person类
Person类设计与Lab1完全一致,首先Person类中有两个属性,分别是名字与朋友列表,分别为String与List。在定义了构造函数的同时,还有三个方法,分别是addFriend,getName,getFriends,功能分别是增加朋友,获得Person的name与获得Person的friend列表。
3.2.3 客户端main()
使用的是Lab1的pdf中给出的测试样例,首先定义Person,接着向生成的graph中添加节点、添加关系并测试两个人之间的关系是不是符合我们预先设定的值。测试结果如下:
3.2.4 测试用例
根据等价类划分,可以分为:无朋友、直接相连的朋友、间接相连的关系。设计的测试样例如下:
3.2.5 提交至Git仓库
如何通过Git提交当前版本到GitHub上你的Lab3仓库。
使用git add ,git commit, git push等命令上传
在这里给出你的项目的目录结构树状示意图。
4 实验进度记录
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
5 实验过程中遇到的困难与解决途径
遇到的难点 解决途径
对于任务不太清晰,同时对于RI等概念不熟悉 对于任务问题多读了几遍要求,并着手完成就逐渐了解了;对于概念问题上课之后有了更深的了解。
对于泛型不是很理解 查找资料之后,比对c++的模板学习,有了更深的了解
6 实验过程中收获的经验、教训、感想
6.1 实验过程中收获的经验和教训
6.2 针对以下方面的感受
(1) 面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?
面向ADT的编程复用性更强,虽然设计过程可能更加繁琐,但是在使用的时候更灵活。
(2) 使用泛型和不使用泛型的编程,对你来说有何差异?
使用泛型设计的时候稍显繁琐,但是使用起来较为方便,不需要为每一种数据类型设计一个类。
(3) 在给出ADT的规约后就开始编写测试用例,优势是什么?你是否能够适应这种测试方式?
优势:对于目标更加清晰,从而编写代码的时候思路更加清晰。正在逐渐适应。
(4) P1设计的ADT在多个应用场景下使用,这种复用带来什么好处?
代码量减少,同时使用的时候也更加方便也显得更加统一。
(5) P3要求你从0开始设计ADT并使用它们完成一个具体应用,你是否已适应从具体应用场景到ADT的“抽象映射”?相比起P1给出了ADT非常明确的rep和方法、ADT之间的逻辑关系,P3要求你自主设计这些内容,你的感受如何?
正在逐渐适应;自主设计的时候感觉还是有一定难度的,需要在不断练习中不断熟悉。
(6) 为ADT撰写specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后编程中坚持这么做?
意义:防止出现不安全的代码;愿意
(7) 关于本实验的工作量、难度、deadline。
适中,可以接受
(8) 《软件构造》课程进展到目前,你对该课程有何体会和建议?
软件构造是大学至今第一门真正接触ADT和OOP设计模式的课程,并在实验课中亲身体会,有了更加深刻的认识,收获很大。