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.2 Implement GraphPoet· 2
训练抽象数据类型(ADT)的设计、规约、测试,并使用面向对象 编程(OOP)技术实现 ADT。
1. 针对给定的应用问题,从问题描述中识别所需的 ADT;
2. 设计 ADT 规约(pre-condition、post-condition)并评估规约的质量;
3. 根据 ADT 的规约设计测试用例;
4. ADT 的泛型化;
5. 根据规约设计 ADT 的多种不同的实现;针对每种实现,设计其表示 (representation)、表示不变性(rep invariant)、抽象过程(abstraction function)
6. 使用 OOP 实现 ADT,并判定表示不变性是否违反、各实现是否存在表 示泄露(rep exposure);
7. 测试 ADT 的实现并评估测试的覆盖度;
8. 使用 ADT 及其实现,为应用问题开发程序;
9. 在测试代码中,能够写出 testing strategy 并据此设计测试用例。
简要陈述你配置本次实验所需环境的过程,必要时可以给出屏幕截图。
特别是要记录配置过程中遇到的问题和困难,以及如何解决的。
在这里给出你的GitHub Lab2仓库的URL地址(Lab2-学号)。
ComputerScienceHIT/HIT-Lab2-120L021525 (github.com)
请仔细对照实验手册,针对两个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
在这里简要概述你对该任务的理解。
本次实验给出了一个图接口,要求我们建立一个边和一个点类分别继承自这个图接口,实现抽象数据型,并完成poem。
如何从GitHub获取该任务的代码、在本地创建git仓库、使用git管理本地开发。
根据实验手册地址下载,使用git。
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
思路:针对Graph<String> 里面的方法进行逐一测试
过程与结果如下:
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
思路:
设计这一部分得先设计每一条边的组成,然后组合起来形成一幅图。
设计每一条边得先设计存储方式,怎么实现一些功能,比如获得source节点,获得target节点,然后在ConcreteEdgesGraph 主函数里面调用每一条边,并且结合起来。
过程:
- 实现EDGE类:
- 初始化边:加权有向边应该要存在应该source和target节点,以及权重,设计如下:
- 函数设计:
函数名称 | 函数作用 |
checkRep() | 检查不变性,节点不空,权重大于0 |
Edge(L source,L target , int weight ) | 构造函数 |
getTarget() | 获得边的target |
getSource() | 获得边的source |
getWeight() | 获得边的权重 |
toString() | 打印边的信息 |
- AF 和 RI 以及防止表示泄露
- 实现ConcreteEdgesGraph类
- 初始化,存储顶点信息,以及边信息
- 函数重写设计
函数名称 | 函数用途 |
ConcreteEdgesGraph | 构造函数 |
checkRep | 检查函数 |
add | 增加顶点 |
Set | 增加边以及边顶点,以source为起点,target为终点,weight为权重构造边,如果存在边,那么更新边信息 |
remove | 移除顶点,并且还需要移除与其相连的顶点的边。 |
vertices | 返回所有顶点(需要clone保护) |
Sources(L target) | 返回一个map存储以target为指向的source节点以及权重 |
Target(L source) | 返回一个map存储以source为起始的target节点以及权重 |
toString | 使用字符串返回图构成信息 |
- AF和RI 以及防止表示泄露
- 测试
一些测试策略
所有测试结果
测试覆盖率:
思路:实现该类需要先将节点信息实现,也就是先实现Vertics类,然后通过该类引用会更加方便
过程:
- 实现 Vertics 类
- 初始化变量信息:
- 函数构造
函数名称 | 函数用途 |
Vertex(L vertex) | 构造函数 |
getName() | 获得顶点的name |
checkRep() | 检查Rep |
tosource(L source) | 返回某个source到name的边的权重,如果不存在则返回0. |
totarget(L target) | 返回name到某个target的边的权重,如果不存在则返回0. |
addsource(L source , int weight) | 给name节点添加一个source节点,以及边权重,存储在sourcemap里面,如果已经存在,并且weight大于0则更新边权重,等于0则remove该点 |
addtarget(L target , int weight) | 给name节点添加一个target节点,以及边权重,存储在targetmap里面,如果已经存在,并且weight大于0则更新边权重,等于0则remove该点 |
removeSource(L source ) | 移除name节点的source节点 |
removeTarget(L target ) | 移除name节点的target节点 |
toString() | 返回每个顶点的所有连接点,已经两者之间的权重 |
- AF,RI以及防止表示泄露:
- ConcreteVerticesGraph 类
- 初始化变量:
- 函数重写设计
函数名称 | 函数作用 |
ConcreteEdgesGraph() | 构造函数 |
checkRep() | 检查函数,保持不变性 |
add(L vertex) | 增加节点 |
set(L source, L target, int weight) | 增加一条以source为起点,以target为终点,权重为weight的边,如果已经存在该边,那么更新权重为weight,weight==0的时候去除该边 |
remove(L vertex) | 去除一个顶点,并且也要去除以该顶点为source,target的顶点 |
vertices() | 返回所有顶点 |
sources(L target) | 返回所有以target为终点的起点集合 |
targets(L source) | 返回所有以source为起点的终点集合 |
toString() | 返回所有图信息 |
- AF,RI和防止表示泄露
- 测试;
测试策略:
测试结果:
代码覆盖度测试:
把string改为L即可实现泛型。
构造一个空的图:
测试策略:
测试结果:
先调用以上实现的图来给字符串形成图,然后调用图生成新的字符串。
过程:
- 初始化:
- 函数设计:
函数名称 | 函数作用 |
GraphPoet(File corpus) | 使用该函数读取文件corpus并且构成一幅图,每个节点都是一个word,每两个word连在一起重复一遍,权重加一 |
poem(String input) | 输入类型为字符串,通过上面函数构成的图来构成一句话,不过只能两个单词只有一个间隔,如果存在有两种情况的word那么选择其中权重较小的word节点 |
结果:完成测试运行
在提供代码的基础上增加一个toString的输出
Main函数调用结果:
请按照Problem Set 2: Poetic Walks的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
命令行:
Git add Lab2-120L021525
Git commit -m “P1”
Git push
在这里简要概述你对该任务的理解。
实验要求我们使用Poetic Walks中已经实现的Graph<L>及其两种图,边图和点图,(在这使用的是点图),实现lab1种实现的功能,并且尽可能复用lab2-P1中已经实现的方法,然后运行提供的main()和执行Lab1中的Junit测试用例,使之正常运行。
给出你的设计和实现思路/过程/结果。
思路:这个类实现的功能有:增加节点、增加边,这两个函数都是直接调用P1已经实现了的功能,直接复用,然后是求距离,使用bfs搜索求距离
过程:
- 数据初始化:
- 函数设计:
函数名称 | 函数作用 |
getShipgraph() | 获得在该类里面建立的图 |
addVertex(Person p1) | 增加节点 |
addEdge(Person p1,Person p2) | 增加边,里面复用了P1中实现的set函数,weight恒为1 |
getDistance(Person p1 , Person p2 ) | 求P1到p2 的距离 |
- 测试
测试策略:
测试结果:
覆盖度测试:
给出你的设计和实现思路/过程/结果。
Person类实现较为简单:
-
-
- 客户端main()
-
给出你的设计和实现思路/过程/结果。
Main()就是加入点,加入边,功能已经在FriendshipGraph类实现。
给出你的设计和实现思路/过程/结果,覆盖度报告。
测试通过:
-
-
- 提交至Git仓库
-
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
Git commit -m “P2”
Git push
- 实验进度记录
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
日期 | 时间段 | 计划任务 | 实际完成情况 |
5.15 | 14:00-17:00 | 阅读实验任务 | 按时完成 |
5.22 | 5月15日-5月22日 | 完成3.1 | 按时完成 |
5.29 | 5月22日-5月29日 | 完成3.2 | 按时完成 |
遇到的难点 | 解决途径 |
不懂RI,AF,表示泄露,都并不会写,也不会写文档 | 网上查资料,网课 |
抽象类的表示泄露预防以及不可变类型知识点不熟悉 | 看网课 |
学会了设计 ADT 规约并评估规约的质量,学会了ADT泛型化,学会并熟练编写测试用例,更加理解可变类型和不可变类型,学会查看代码覆盖度。学会编写和设计AF,RI以及如何防止表示泄露。
- 面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?
面向ADT编程代码量更多。
- 使用泛型和不使用泛型的编程,对你来说有何差异?
泛型适应性更强,但是限制也更多,非泛型不需要考虑到其他类型能不能使用。
- 在给出ADT的规约后就开始编写测试用例,优势是什么?你是否能够适应这种测试方式?
能够更有针对性得设计测试用例。
- P1设计的ADT在多个应用场景下使用,这种复用带来什么好处?
代码的可移植性好,如果换一个数据类型,不需要重写代码。
- 为ADT撰写specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后编程中坚持这么做?
防止表示泄露是非常重要的一件事,如果不CheckRep万一发生表示泄露那么很难找到源头在哪,我们又得重新一句一句去找表示泄露处。以后编程也会坚持。
- 关于本实验的工作量、难度、deadline。
P1 工作量较大,难度也较大,P2较小。由于其他课程实验及考试,时间还是比较紧张。
- 《软件构造》课程进展到目前,你对该课程有何体会和建议?
希望老师可以多讲一些例子。