本次实验训练抽象数据类型(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.实验环境配置
延续Lab1中的实验环境,除此之外,本次实验在 Eclipse IDE中安装配置 EclEmma(一个用于统计JUnit测试用例的代码覆盖度测试用例的代码覆盖度plugin)
在这里给出你的GitHub Lab2仓库的URL地址(Lab2-学号):
https://github.com/ComputerScienceHIT/Lab2-1173710111
3.实验过程
请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)
3.1Poetic Walks
- 对给定的接口Graph<L>,完成他的两个实例类ConcretVerticesGraph和ConcreateEgdesGraph:
(1)首先分别构造两个实例类的rep类Vertex和Edge;
(2)用String作为L的特例,分别在两个实例类中实现Graph接口中的各个方法,写对应的test文件以确保其正确性;
(3)将所有String变量替换为泛型L。
2. 对给定的文本文件,按照给定规则建立有向图,调用Graph<L>作为存储语料库的数据结构,利用该结构,对输入的字符串进行扩充。
3.1.1Get the code and prepare Git repository
请阅读 http://web.mit.edu/6.031/www/sp17/psets/ps2/,遵循该页面内的要求完 ,遵循该页面内的要求完成编程任务。
⚫ 在 Get the code步骤中你无法连接MIT的 Athena服务器,请从以下地址获取初始代码 :
https://github.com/rainywang/Spring2019_HITCS_SC_Lab2/tree/master/P1
⚫ 在作业描述中若遇到 “commit and push”的要求 ,请将你的代码 push到你的 GitHub Lab2仓库中 。
⚫ 其他步骤请遵循 MIT。
自 https://github.com/rainywang/Spring2019_HITCS_SC_Lab2/tree/master/P1 获得实验代码。
git初始化:
Git init
Git remote add origin git@github.com:ComputerScienceHIT/Lab2-1173710111.git
Git pull origin master
Git add .
Git commit -m “xxx”
Git push origin master
3.1.2Problem 1: Test Graph <String>
静态 Graph.empty()方法的测试策略和测试都在GraphStaticTest.java,为了运行这个测试,首先要修改Graph<L> empty()为:
public static String Graph empty() {
Graph graph = new ConcreteEdgesGraph();
return graph;
}
这里以ConcreteEdgesGraph作为Graph默认的实例类,也可以用ConcreteVerticesGraph,二者是等价的
3.1.3 Problem 2: Implement Graph <String>
3.1.3.1Implement ConcreteEdgesGraph
(1)实现 Edge<String>类
- rep:
private int weight; |
边的权值 |
private String sourceVertex; |
边的起点 |
private String targetVertex; |
边的终点 |
- constructor:
public Edge(L source,L target,int w){ this.weight=w; this.sourceVertex=source; this.targetVertex=target; }
|
- function:
getter |
三个域的getter,因为规定Edge<L>是Immutable的所以没有setter。 |
public void checkRep() { assert sourceVertex!=null; assert targetVertex!=null; assert weight>=0; } |
每个getter返回前检查该边的起点、终点、权值是否有空的。 |
@Override public String toString() |
以字符串的形式直观地展示该边的起点、终点和边权,格式为: source->target [weight] |
(2)实现ConcreteEdgesGraph类
- rep:
private final Set<String> vertices |
保存图中的所有顶点 |
private final List<Edge<String>> edges |
顺序存储图中的所有边 |
- function:
每个function返回前检查rep不变 private void checkRep() { for(int i=0;i<edges.size();i++) { assert vertices.contains(edges.get(i).getSourceVertex()):"an edge with illegal vertex"; assert vertices.contains(edges.get(i).getTargetVertex()):"an edge with illegal vertex"; for(int j=i+1;j<edges.size();j++) { assert !(edges.get(i).getSourceVertex().equals(edges.get(j).getSourceVertex())&&edges.get(i).getTargetVertex().equals(edges.get(j).getTargetVertex())) :"duplicate edges"; } assert edges.get(i)!=null; } for(L s:vertices) { assert s!=null; } } |
|
@Override public boolean add(String vertex) |
(1)点存在:返回false (2)不存在:调用list.add向vertices中添加该点 |
@Override public int set(String source, String target, int weight) |
遍历edges: (1)如果已经存在从source到target的一条边,记下这条边的权值,创建一条新的边(参数为source,target,weight),调用Collections.replaceAll替换掉edges中的旧边,返回旧边权; (2)如果不存在,新建一条边(参数为source,target,weight),将其加入edges,返回0。 |
@Override public boolean remove(L vertex) |
调用set.contains判断图中是否存在该顶点:(1)如果不存在,返回false;
|
@Override public Set<L> vertices() |
copy一份vertices到新Set中,返回这个Set |
@Override public Map<L, Integer> sources(L target) |
建立一个空Map,遍历edges,找出以target为终点的边edge; weight=0时表示该边不存在,跳过; 将edge.source->edge.weight加入Map,遍历结束后返回这个Map |
@Override public Map<L, Integer> targets(L source) |
建立一个空Map,遍历edges,找出以source为起点的边edge; weight=0时表示该边不存在,跳过; 将edge.source->edge.weight加入Map,遍历结束后返回这个Map |
@Override public String toString() |
将所有边的toString连接在一起 |
(3)编写测试 GraphInstanceTest.java
public abstract Graph<String> emptyInstance(); |
抽象方法,创建新的空Graph<String> |
@Test public void testAdd() |
分别对input未存在、已存在的情况进行测试,检查其rep是否与期待相符 |
@Test public void testRemove() |
|
@Test public void testSources() |
1. 参数vertex的source域为空; 2. 参数vertex的target域为空; 3. 对set方法传入weight=0的情况进行测试
方法调用后,对相关参数(边)调用source方法或target方法的返回值进行检查 |
@Test public void testTargets() |
|
@Test public void testSet() |
方法调用后,对相关参数(边)调用source方法或target方法的返回值进行检查 |
@Test public void testVertices() |
调用add和remove方法,然后对vertices方法的返回值进行检查 |
@Test public void testALL() |
创建一个较复杂的有向图,调用到每个方法,对rep进行检查 |
(4)编写测试ConcreteEdgesGraphTest.java
Graph中定义的接口 |
GraphInstanceTest.java中实现,不必重复 |
创建一个新的空图 ConcreteEdgesGraph @Override public Graph<String> emptyInstance() { return new ConcreteEdgesGraph<String>(); }
|
|
public void testToString() |
调用Graph中的方法构建一张有向图,测试toString的返回值是否能正确规范地表示这张图。 |
public void testVertex() |
主要测试Edge中的toString方法,测试其返回值能否正确规范地表示这条边的信息。 |
3.1.3.2Implement ConcreteVerticesGraph
(1)实现Vertex<L>类
- rep:
private L name; |
当前节点的名字(标签) |
private Map<L, Integer>source; |
存储所有以当前点为终点的边,key表示边的起点,value表示边权 |
private Map<L, Integer>target; |
存储所有以当前点为起点的边,key表示边的终点,value表示边权 |
- constructor:
public Vertex(L name){ this.name=name; } |
- function:
setter |
source,target的setter |
getter |
name,source,target的getter |
public void checkRep() { assert source!=null; assert target!=null; assert name!=null |