3.1.1 Get the code and prepare Git repository 2
3.1.2 Problem 1: Test Graph <String> 2
3.1.3 Problem 2: Implement Graph <String> 5
3.1.3.1 Implement ConcreteEdgesGraph 5
3.1.3.2 Implement ConcreteVerticesGraph 8
3.1.4 Problem 3: Implement generic Graph<L> 12
3.1.4.1 Make the implementations generic 12
3.1.4.2 Implement Graph.empty() 13
3.1.5 Problem 4: Poetic walks 13
3.1.5.2 Implement GraphPoet 14
本次实验训练抽象数据类型(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.实验环境配置
本次实验中无需额外配置,之前已经装好。
在这里给出你的GitHub Lab2仓库的URL地址(Lab2-学号)。
https://github.com/ComputerScienceHIT/HIT-Lab2-hit1190200203
3.实验过程
请仔细对照实验手册,针对两个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1Poetic Walks
本次实验给我们了一个图接口,要求建立一个边图类和一个点图类分别继承这个图的接口,并且实现一系列方法和抽象数据型,并用这个图的抽象数据型完成poet的工作。主要是为了练习使用ADT的规约设计和ADT的实现。
3.1.1Get the code and prepare Git repository
老师给的网址:
https://github.com/rainywang/Spring2021_HITCS_SC_Lab2/tree/master/P1
在网址上下载文件,然后放到自己的git仓库中,创建项目。
3.1.2Problem 1: Test Graph <String>
思路:这是对Graph<String>设计的测试策略,针对里面的每个方法进行等价类划分的测试评价。
过程:如下图为所有方法的测试方法:





覆盖率:


3.1.3Problem 2: Implement Graph <String>
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
3.1.3.1Implement ConcreteEdgesGraph
1.实现Edge类:
(1).类中的字段应该包括边的长度,边的起点和终点,所以定义私有类型的这些变量如下图:
(2).实现的方法为:
| Edge | 初始化构造方法,构造新边的两个点和该边的权值 |
| checkRep | 检查表示不变性,边不能是空且权值应大于等于0 |
| getsource | 返回边的一个起点 |
| gettarget | 返回边的终点 |
| getweight | 返回边的权值 |
| toString | 返回边的字符串,形式为“起点->终点权重为xx” |
(3).对于Abstraction function、Representation invariant、Safety from rep exposure如下图:

- 测试方法:每个只需测试一次就行



2.实现ConcreteEdgesGraph类:
(1).ConcreteEdgesGraph的字段中应包括顶点set的表和边list的表,定义的表如下图所示:

(2).在ConcreteEdgesGraph中需要实现的方法如下图所示:
| ConcreteEdgesGraph | 构造方法 |
| checkRep | 检查不变性,edges长度应是大于0的实数,并且是有起始的节点 |
| add | 当顶点不为空时,把一个顶点加入到点表里 |
| set | 输入边的起点、终点和权值。若权值为负,则返回-1。若权值为正且新边已经存在,则除去原来的边并加上新边。若权值为正但新边不存在,则直接加上新边。若权值是0且新边已经存在,则去除原边。只要改变了原边的权值都返回原边权值,没有权值则返回0 |
| remove | 除去点及与它相连的所有边。需要遍历edges,寻找是否有边的起点或者终点是该点,然后删去即可,使用迭代器实现。 |
| vertices | 返回所有的点集 |
| sources | 输入一个终点,返回与它相连的所有边和起点构成的Map |
| targets | 输入一个起点,返回与它相连的所有边和终点构成的Map |
| toString | 将整个图中所有点的转化为一条字符串输出 |
(3).对于Abstraction function、Representation invariant、Safety from rep exposure如下图:
(4).测试结果为:
覆盖率为:
3.1.3.2Implement ConcreteVerticesGraph
1.实现Vertices类:
(1).Vertices的字段中应包括点的名字、点的源点表Map和点的终点表Map,定义的表如下图所示:

(2).在Vertices需要实现的方法如下图所示:
| Vertex | 初始化构造方法 |
| checkRep | 检查表示不变性,每个边权值应该大于0 |
| getmark | 返回点的名字 |
| getsource | 返回能到达该点的所有点和边构成的Map |
| gettarget | 返回一个点能到达的所有点和边构成的Map |
| addsource | 在源点Map中加入某源点,若权值不是0,则将其加入源点中,若源点已存在,则更新权值并返回原来的权值,不存在则直接构建新点并返回0;若权值为0,则移除源点。 |
| addtarget | 在终点Map中加入某终点,若权值不是0,则将其加入终点中,若终点已存在,则更新权值并返回原来的权值,不存在则直接构建新点并返回0;若权值为0,则移除终点。 |
| removesource | 在源点表中删除某个源点并返回原来的边长 |
| removetarget | 在终点表中删除某个终点并返回原来的边长 |
| toString | 一个点的字符串的表示 |
(3)对于Abstraction function、Representation invariant、Safety from rep exposure如下图:
(4)测试方法:



2.实现ConcreteVerticesGraph类:
(1).ConcreteVerticesGraph的字段为Vertex构成的List,定义的表如下图所示:
(2).在ConcreteVerticesGraph需要实现的方法如下图所示:
| ConcreteVerticesGraph | 初始化构造方法 |
| checkRep | 检查表示不变性,vertices中不能有重复点 |
| add | 顶点不空时,加入一个顶点进入点表中 |
| set | 输入边的起点、终点和权值。若权值为负,则返回-1。若权值为正且新边已经存在,则除去原边并加上新边。若权值为正且新边不存在,则直接加上新边。若权值为0且新边已经存在,则去除原边。只要改变了原边权值都返回原边权值,没有权值则返回0 |
| remove | 除去点及与它相邻的所有边。需要遍历vertices,寻找是否有与待删除点相同的名字的点直接删去即可,如果名字不相同,则在该点的源点表和终点表中寻找删去即可,使用迭代器实现。 |
| vertices | 返回所有的点集 |
| sources | 输入一个终点返回与它相连的所有边和起点构成的Map |
| targets | 输入一个起点返回与它相连的所有边和终点构成的Map |
| toString | 所有点转化为一条字符串输出 |
(3)对于Abstraction function、Representation invariant、Safety from rep exposure如下图:

(4).测试:
和上面一样,只需要加上toString 的测试
测试结果为:
覆盖率:
3.1.4Problem 3: Implement generic Graph<L>
3.1.4.1Make the implementations generic
泛型完成替换即可:



3.1.4.2Implement Graph.empty()
只需调用一个具体的实现:
测试策略:

3.1.5Problem 4: Poetic walks
任务要求我们实现一个类,利用之前实现的图结构,解释了诗人如何用语料库初始化,然后在给定输入的情况下,使用由该语料库定义的词亲和图来诗意地转换输入。
3.1.5.1Test GraphPoet
测试方法:
1.testGraphPoet()
按需要读入的文件划分:空文件,一行输入,多行输入
2.testPoem()
按图中边权值划分:权值全为1,两点之间权值有不为1且有几条边
- testtoString()
按需要读入的文件划分:空文件,不是空文件
实现:


结果:

覆盖率:

3.1.5.2Implement GraphPoet
(1). GraphPoet的字段为String构成的Graph,定义的表如下图所示:

(2)对于Abstraction function、Representation invariant、Safety from rep exposure如下图:

(3).在GraphPoet中需要实现的方法如下图所示:
| GraphPoet | 输入文件的路径,按行读入,存在List中,然后取相邻的元素,添加新的边到图中 |
| checkRep | 检查不变性,保存从语料库文件生成的图 |
| poem | 输入进行扩充的字符串,声明一个StringBuilder保存,每次读取一个词,当前词作为起点,下一个词作为终点,然后在图中寻找起点的终点表中是否有与终点的源点表中相同的元素,并找到权值最大的点加入起点和终点之间,返回扩充后的字符串 |
| toString | 调用ConcreteEdgesGraph中的toString方法,将图中所有点转化为一条字符串输出 |
3.1.5.3Graph poetry slam
在提供main代码的基础上增加一个toString的输出。
原来为:

增加后为:
3.1.6Before you’re done
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
通过git add *->git commit -m “”->git push提交到Lab2仓库
在这里给出你的项目的目录结构树状示意图。

3.2Re-implement the Social Network in Lab1
这次实验要求基于定义的Graph<L>及其两种实现,实现Lab1中Social NetWorek中的各种功能,并且尽可能复用ConcreteVerticesGraph<L>中已经实现的方法,然后运行提供的main()和执行Lab1中的Junit测试用例,使之正常运行。
3.2.1FriendshipGraph类
(1).FriendshipGraph的字段为Person构成的ConcreteEdgesGraph,定义表如下图所示:

(2).在FriendshipGraph需要实现的方法如下图所示:
| FriendshipGraph | 初始化构造方法 |
| addVertex | 在图中增加新人,只需要调用ConcreteEdgesGraph中的add |
| addEdge | 为x增加朋友y,直接调用ConcreteEdgesGraph中的set |
| getpeople | 直接返回 |
| getDistance | 得到两个人之间的最短距离。与Lab1类似,稍加修改: 实现此功能用先广方式求最短路径的方法。首先我们需要知道若两个Person对象是同一个,则返回0,然后定义一个Map集合way和一个Person队列queue,队列queue用来储存广搜的遍历结果,way的Map集合用来储存广搜的所有元素及他们与第一个元素的距离。具体实现方法为首先将第一个元素d1入队,并且把第一个元素d1和下标0入集合,当队列非空时,弹出队首元素t,并且得到t在集合way中的下标distance,然后定义一个Map为friend,执行personGraph.targets(top)得到队首元素的所有朋友的Map,然后定义Set为allfriend,对刚才的friend执行keySet()得到allfriend,只要allfriend中的元素与d不同,就把这些元素全部入队,并且把这些元素及下标distance+1放入集合。只要队列非空,继续执行以上步骤,直到找到某个元素与d2相同并且返回这个元素在集合way中的下标。如果直到队列为空还没找到d2,则返回-1: |
| main | 复制Lab1 |
(3).对于Abstraction function、Representation invariant、Safety from rep exposure如下图:
3.2.2Person类
给出你的设计和实现思路/过程/结果。
(1).person类型为:

(2)实现方法:
| Person | 没有重复名字则加入 |
| getmyname | 返回自己的名字 |
(3).对于Abstraction function、Representation invariant、Safety from rep exposure如下图:
3.2.3客户端main()
给出你的设计和实现思路/过程/结果。
按照Lab1中的main复制过来即可:
结果为:
3.2.4测试用例
给出你的设计和实现思路/过程/结果。
于Lab1相同:




结果为:
覆盖率: 
3.2.5提交至Git仓库
如何通过Git提交当前版本到GitHub上你的Lab3仓库。
通过git add *->git commit -m “”->git push提交到Lab2仓库
在这里给出你的项目的目录结构树状示意图。

4.实验进度记录
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
| 日期 | 时间段 | 计划任务 | 实际完成情况 |
| 2021.5.25 | 18:30-20:10 | 3.1-3.1.3.1 | 延时两个小时完成 |
| 2021.6.1 | 18:30-20:10 | 3.1.3.2-3.1.4.2 | 延时一天完成 |
| 2021.6.5 | 15:00-21:00 | 3.1.4.-3.1.6 | 准时完成 |
| 2021.6.6 | 15:00-17:00 | 3.2-3.2.2 | 延时一个小时完成 |
| 2021.6.8 | 18:30-20:10 | 3.2.3-3.2.5 | 准时完成 |
5.实验过程中遇到的困难与解决途径
| 遇到的难点 | 解决途径 |
| 刚开始写时,有的内容还没有讲解,压根不知道是什么 | 后来上课老师讲了就明白了 |
| 不知道泛型是什么 | 通过同学的帮助和查找资料理解了 |
| 不太明白接口 | 通过博客园明白了 |
| P2有些P1的内容忘记了 | 通过看自己的报告和代码,想起来了 |
6.实验过程中收获的经验、教训、感想
6.1实验过程中收获的经验和教训
先设计测试时,要明白任务要做什么,此外设计多种类来实现功能的情况时,自己设计的时候很多概念不明白,导致有很多的错误,很多关系也弄不清楚。主要是因为自己课程理解不到位和对Java语言掌握不熟练导致的,要注重基础的知识才可以。
6.2针对以下方面的感受
1.面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?
面向对象能够每次完成对某个对象的编程,与面向ADT的编程的思路完全不一样。
2.使用泛型和不使用泛型的编程,对你来说有何差异?
泛型能够适应更多情况
3.在给出ADT的规约后就开始编写测试用例,优势是什么?你是否能够适应这种测试方式?
能够保证代码的正确性并及时修改。非常不适应,一开始无从下手
4.P1设计的ADT在多个应用场景下使用,这种复用带来什么好处?
能够提高代码的利用率,减少重复
5.为ADT撰写specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后编程中坚持这么做?
最开始一头雾水,后来通过慢慢分析对每个类进行了实现,但是还是很多方法用的很繁琐,并且很多类很重复,一些功能没有用到
6.关于本实验的工作量、难度、deadline。
工作量很大,难度还可以,主要写测试太难了,报告的内容冗杂,需要截图。deadline还可以
7.《软件构造》课程进展到目前,你对该课程有何体会和建议?
希望老师可以将的慢一点,有点跟不上老师讲的内容
如果可以希望老师多讲些Java
本次实验重点在于设计和实现抽象数据类型(ADT),包括图接口及其两种实现,以及在社交网络场景的应用。实验过程中,通过泛型、测试用例设计、表示不变性检查等手段确保ADT的正确性和灵活性。同时,重新实现了Lab1的社交网络功能,加深了对面向ADT编程的理解。实验中遇到的主要困难是理解和应用泛型,以及设计测试用例,但通过查阅资料和同学帮助得以解决。实验收获在于认识到ADT设计的重要性以及测试先行的必要性。

634

被折叠的 条评论
为什么被折叠?



