2021年春季学期
计算学部《软件构造》课程
Lab 2实验报告
目录
1 实验目标概述 1
2 实验环境配置 1
3 实验过程 1
3.1 Poetic Walks 1
3.1.1 Get the code and prepare Git repository 1
3.1.2 Problem 1: Test Graph <String> 1
3.1.3 Problem 2: Implement Graph <String> 1
3.1.3.1 Implement ConcreteEdgesGraph 2
3.1.3.2 Implement ConcreteVerticesGraph 2
3.1.4 Problem 3: Implement generic Graph<L> 2
3.1.4.1 Make the implementations generic 2
3.1.4.2 Implement Graph.empty() 2
3.1.5 Problem 4: Poetic walks 2
3.1.5.1 Test GraphPoet 2
3.1.5.2 Implement GraphPoet 2
3.1.5.3 Graph poetry slam 2
3.1.6 使用Eclemma检查测试的代码覆盖度 2
3.1.7 Before you’re done 2
3.2 Re-implement the Social Network in Lab1 2
3.2.1 FriendshipGraph类 2
3.2.2 Person类 3
3.2.3 客户端main() 3
3.2.4 测试用例 3
3.2.5 提交至Git仓库 3
4 实验进度记录 3
5 实验过程中遇到的困难与解决途径 3
6 实验过程中收获的经验、教训、感想 4
6.1 实验过程中收获的经验和教训 4
6.2 针对以下方面的感受 4
1实验目标概述
本次实验训练抽象数据类型(ADT)的设计、规约、测试,并使用面向对象编程(OOP)技术实现ADT。具体来说:
针对给定的应用问题,从问题描述中识别所需的ADT;
设计ADT规约(pre-condition、post-condition)并评估规约的质量;
根据ADT的规约设计测试用例;
ADT的泛型化;
根据规约设计ADT的多种不同的实现;针对每种实现,设计其表示(representation)、表示不变性(rep invariant)、抽象过程(abstraction function)
使用OOP实现ADT,并判定表示不变性是否违反、各实现是否存在表示泄露(rep exposure);
测试ADT的实现并评估测试的覆盖度;
使用ADT及其实现,为应用问题开发程序;
在测试代码中,能够写出testing strategy并据此设计测试用例。
2实验环境配置
在 Eclipse IDE 中安装配置 EclEmma(一个用于统计 JUnit 测试用例的代码覆盖度的 plugin)直接从Eclipse Market下载安装即可。
在这里给出你的GitHub Lab2仓库的URL地址(Lab2-学号)。
https://github.com/ComputerScienceHIT/HIT-Lab2-1190202407
3实验过程
请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1Poetic Walks
该任务是设计一个图模块,实现以下功能
1.完善Graph接口类
2.实现Graph类,完善add、set、remove、sources、targets等
3.利用Graph类,实现GraphPoet类
3.1.1Get the code and prepare Git repository
git init
git remote add origin git@github.com:ComputerScienceHIT /Lab2-1190202407. git pull origin master
git add .
git commit -m “init”
git push origin master
3.1.2Problem 1: Test Graph <String>
测试add,set,remove、sources、targets等函数
add划分输入:vertex:已存入graph的点,未存入的点
set划分输入:source,target:已存入的点,未存入的点
weight:0,非0
remove划分输入:vertex:已存入,未存入,与其他点相连的点
vertices划分输入:graph:空、非空图
sources划分输入:target:有边连接的点,无边连接的点
target划分输入:source:有边连接的点,无边连接的点
3.1.3Problem 2: Implement Graph <String>
该部分要求实现Graph Interface里的方法,要求实现ConcreteEdgesGraph与ConcreteVerticesGraph。
3.1.3.1Implement ConcreteEdgesGraph
Edge类设计:
Fields 表示
source 边的起始点
target 边的结束点
weight 边的权值
Methods 作用
Getsource 返回有向边起始节点
Gettarget 返回有向边终结点
Getweight 返回边权值
Tostring 描述边
Concreteedgesgraph类设计:
Fields 用途
Edges 存储顶点
Vertices 存储边
Method 作用
public boolean add(L vertex) 向图中添加一个点
public int set(L source, L target, int weight) 对图进行操作
public boolean remove(L vertex) 从图中移除一个点,及其所关联的边
Public set vertices() 返回图中的点集
public Map<L, Integer> sources(L target) 返回target点所有的入边
public Map<L, Integer> targets(L source) 返回图的string表示
Set设计思路:先判断要添加的边是否已在图中,若起点与终点间权值不为0,则更新权值并返回之前的权值;为0,则删去该边;若不存在,则创建一个新的edge,存储其各信息。
remove的设计思路为:先从点集vertices中删除该点,然后再删除与该点有关的所有边。
3.1.3.2Implement ConcreteVerticesGraph
Vertex类:
Fields 表示
Vertex 当前节点
Outedges 节点的出边
Inedges 节点的入边
Method 作用
Private void checkRep() 检查节点合法性
public Map<L, Integer> sources(L target) 根据传入的target参数寻找以targe为终点的边,返回一个权值对为(点,权重)的map
public Map<L, Integer> targets(L source) 根据传入的source参数寻找以source为起点的边
public boolean remove(L vertex) 删除该节点一条边
public boolean set(L source, L target,int weight) 给当前节点添加一条边
ConcreteVerticeGraph
Method 作用
public boolean add(L vertex) 向图中添加节点
public int set(L source, L target, int weight) 对图进行相关操作
public boolean remove(L vertex) 从图中删除一个点
public Set vertices() 返回图中的点集
public Map<L, Integer> sources(L target) 返回target点的入边
public Map<L, Integer> targets(L source) 返回source点的出边
3.1.4Problem 3: Implement generic Graph<L>
为了使我们的Graph更加地实用,将原本使用String改为使用泛型<L>。
3.1.4.1Make the implementations generic
使用泛型,将原代码中一些原本使用String的地方对应改为泛型的placeholder <L>。
3.1.4.2Implement Graph.empty()
3.1.5Problem 4: Poetic walks
该部分需要我们将前述实现的Graph应用到Poetic walks中,用于将语料库(corpus)转化为Graph,再根据这个Graph将一个输入的String,转换成一个新的String(poem)。
3.1.5.1Test GraphPoet
测试结果:
覆盖率:
3.1.5.2Implement GraphPoet
需要一个类型为String的graph
需要实现的方法
Method 作用
3.1.5.3Graph poetry slam
在给定代码的基础上增加一个toString方法来输出
运行结果:
3.1.6使用Eclemma检查测试的代码覆盖度
3.1.7Before you’re done
请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
在这里给出你的项目的目录结构树状示意图。
3.2Re-implement the Social Network in Lab1
继承P1中ConcreteEdgesGraph , ConcreteVerticesGraph类,实现FriendshipGraph,通过基本操作实现FriendshipGraph中addVertex,addEdge和getDistance三个接口。
3.2.1FriendshipGraph类
FriendshipGraph的字段为Person构成的ConcreteEdgesGraph
在FriendshipGraph需要实现的方法如下图所示:
Method 构造方法
addVertex 在图中增加新Person,只需要调用ConcreteEdgesGraph中的add即可
addEdge 为某个人增加朋友,a为这个人,b为其新增的朋友,直接调用ConcreteEdgesGraph中的set即可。
getallfriend 直接返回。
getDistance 得到两个人之间的最短距离,方法与Lab1类似,稍加修改即可:
我们要通过队列,采用广度遍历的思维,把作为起点的人当做p1,终点的人当做p2,先把p1压入队列,然后进入循环,只要队列不为空就不结束循环。还有生成一个叫做len的Map,key的类型是Person,映射到这个人与p1的距离,然后先设一个(p1,0)的映射。接着每次循环一开始都是弹出队列顶部的人,然后把distance基准距离设为这个人在len中映射得到的距离。然后,运用已编写好的方法,生成一个包含这个人所有朋友的集合,遍历这个集合,只要集合中有某人不在len中,就把put到len里,映射的距离是distance+1,同时也把这个人放入队列中。然后,每当一个人的朋友遍历完后,就查看一下len的key中有没有我们要找的p2,如果有,直接返回p2映射到的距离。最后,当最外层的循环结束时,肯定是没有找到p2这个人,那么说明p2与p1间无关系,那么应该返回-1
main 复制Lab1稍作修改。
在FriendshipGraph需要实现的方法如下图所示:
Method 构造方法
addVertex 在图中增加新Person,只需要调用ConcreteEdgesGraph中的add即可。
addEdge 为某个人增加朋友,a为这个人,b为其新增的朋友,直接调用ConcreteEdgesGraph中的set。
getallfriend 直接返回。
getDistance 得到两个人之间的最短距离,方法与Lab1类似,稍加修改。
我们要通过队列,采用广度遍历的思维,把作为起点的人当做p1,终点的人当做p2,先把p1压入队列,然后进入循环,只要队列不为空就不结束循环。还有生成一个叫做len的Map,key的类型是Person,映射到这个人与p1的距离,然后先设一个(p1,0)的映射。接着每次循环一开始都是弹出队列顶部的人,然后把distance基准距离设为这个人在len中映射得到的距离。然后,运用已编写好的方法,生成一个包含这个人所有朋友的集合,遍历这个集合,只要集合中有某人不在len中,就把put到len里,映射的距离是distance+1,同时也把这个人放入队列中。然后,每当一个人的朋友遍历完后,就查看一下len的key中有没有我们要找的p2,如果有,直接返回p2映射到的距离。最后,当最外层的循环结束时,肯定是没有找到p2这个人,那么说明p2与p1间无关系,那么应该返回-1。
main 复制Lab1稍作修改。
3.2.2Person类
Person 没有重复名字则加入,构造方法
getname 返回本人名字
实现方法:
3.2.3客户端main()
运行结果:
3.2.4测试用例
测试策略:与Lab1的测试策略相同,先新建一个graph
覆盖率:
3.2.5提交至Git仓库
项目的目录结构:
项目名称: HIT-Lab2-学号
src
P2
FriendshipGraph.java
Person.java
...
test
P2
FriendshipGraphTest.java
...
4实验进度记录
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
日期 时间段 计划任务 实际完成情况
5.31 14:00-17:00 完成P1中concreateedgesgraph和concreteverticesgraph并进行测试 未完成
6.1 15:30-17:30 完成上述任务 完成
6.3 15:45-17:30 完成poetic walks 未完成
6.4 13:00-15:00 继续完成上述任务 完成
6.6 15:00-16:00 完成P2 完成
5实验过程中遇到的困难与解决途径
遇到的难点 解决途径
对于继承类,接口等概念仍不是十分熟悉 通过上网查阅相关资料加以学习
对于poet如何实现增添词时,用于参考的重复的数量,已经增添词的数量该如何确定,如何实现 上网查询并向同学进行咨询
6实验过程中收获的经验、教训、感想
6.1实验过程中收获的经验和教训
更加熟练使用java编写程序,了解规约过程,理解java封装机制。
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,这些工作的意义是什么?你是否愿意在以后编程中坚持这么做?
更好的理解ADT;不愿意,太麻烦。
(7)关于本实验的工作量、难度、deadline。
时间比较宽松,难度适中。
(8)《软件构造》课程进展到目前,你对该课程有何体会和建议?
建议有中文的相关学习材料,看英文比较费劲。