本次实验
训练抽象数据类型(ADT)的设计、规约、测试,并使用面向对象编程(OOP)技术实现 ADT。具体来说:
针对给定的应用问题,从问题描述中识别所需的ADT
2.1 EclEmma的安装
进入EclEmma官网 https://www.eclemma.org/ 寻找安装方法,
发现可以使用eclipse自带的市场安装,打开搜索。
具体方法:Eclipse -> help -> eclipse market
请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
对给定的接口Graph,完成他的两个实例类ConcretVerticesGraph和ConcreateEgdesGraph:
(1)首先分别构造两个实例类的rep类Vertex和Edge;
(2)用String作为L的特例,分别在两个实例类中实现Graph接口中的各个方法,写对应的test文件以确保其正确性,并且按照要求撰写每个方法的规约,AF,RI,以及rep exposure;
(3)将所有String变量替换为泛型L,实现图的泛型化。
2. 对给定的文本文件,按照给定规则建立有向图,调用Graph作为存储语料库的数据结构,利用该结构,对输入的字符串进行扩充。
阅读http://web.mit.edu/6.031/www/sp17/psets/ps2/ ,遵循该页面内的要求完成编程任务。
获取初始代码:
https://github.com/rainywang/Spring2020_HITCS_SC_Lab2/tree/master/P1
在作业描述中若遇到 “commit and push”的要求 请将你的代码push到你的 GitHub Lab2仓库中。具体步骤为:
Git init
Git remote add origin xxxx你的github的地址
Git pull origin master
Git add .
Git commit -m “xxx”
Git push origin master
MIT作业页面提及的文件路径,请按照下表的目录结构进行调整例如test/poet 应为“ test/ P1/ poet , src /poet 应为“ src P1/ poet 。
其他步骤请遵循 MIT作业页面的要求。
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
首先要将泛型graph类型中的静态方法任意返回其中一种具体实现类型
对于非static类型的方法的测试策略
1.测试add方法
add方法:a.The vertex has already included in the graph
b.The vertex has not already included in the graph
2.测试set方法
* set方法:sourcenode targetnode:
* a.sourcenode,targetnode has already included in the graph
* b.one or two of the points has not included in the graph
* weight:
//function
private void checkRep();
public int getweight();
public L getSourcenode();
public L getTargetnode();
public String toString();
各个方法的具体实现这里不一一列出,功能基本较为简单主要为一些getter以及toString方法
//Rep
private final Set vertices = new HashSet();
private final List
//function:
private void checkRep();//每个function返回前检查rep不变
需要检查list中的不重复性,以及每一个edge的两端都在点集中
@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),移除原来存在的边,加入新的一条边,替换掉edges中的旧边,返回旧边权;
(2)如果不存在,新建一条边(参数为source,target,weight),将其加入edges,返回0。
(3)如果weight=0,而且不存在这条边不需要做任何变化,如果存在这条边将这条边删除,并返回此条边的权值。
@Override
public boolean remove(L vertex);
调用set.contains判断图中是否存在该顶点:(1)如果不存在,返回false;
否则,遍历edges,删除起点或终点是该顶点的边,遍历结束后调用list.remove从vertices中删除该顶点。
@Override
public Set vertices();
copy一份vertices到新Set中,返回这个Set.采用防御式拷贝的方式防止不变量的暴露。
@Override
public Map
建立一个空Map,遍历edges,找出以target为终点的边edge;
weight=0时表示该边不存在,跳过;将edge.source->edge.weight加入Map,遍历结束后返回这个Map.
@Override
public Map
建立一个空Map,遍历edges,找出以source为起点的边edge;
weight=0时表示该边不存在,跳过;将edge.source->edge.weight加入Map,遍历结束后返回这个Map.
@Override
public String toString();
将所有边的toString连接在一起并输出。
由于这个类继承了GraphInstanceTest类,因此通用的方法不需再编写测试,只需要测试ConcreteEdgesGraph中特有的方法,其他的方法继承GraphInstanceTest已经实现的方法。
//functions:
private void checkRep()检查不变性
public L getname();
public Map< L, Integer> getsources();
public Map< L, Integer> gettarget();
public void removesource(L source);
public void removetarget(L target);
public void addsource(L source,int weight);
public void addtarget (L target,int weight);
public String toString();//重写tostring方法
//function:
注:由于这个类与edgegraph类等价实现garph接口,因此函数的实现有很大的类似性,这里仅仅写出函数的声明以及一些实现方法。具体代码不再复现。
@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),移除原来存在的边,加入新的一条边,替换掉edges中的旧边,返回旧边权;
(2)如果不存在,新建一条边(参数为source,target,weight),将其加入edges,返回0。
(3)如果weight=0,而且不存在这条边不需要做任何变化,如果存在这条边将这条边删除,并返回此条边的权值。
@Override
public boolean remove(L vertex);
调用set.contains判断图中是否存在该顶点:(1)如果不存在,返回false;
否则,遍历edges,删除起点或终点是该顶点的边,遍历结束后调用list.remove从vertices中删除该顶点。
@Override
public Set vertices();
copy一份vertices到新Set中,返回这个Set.采用防御式拷贝的方式防止不变量的暴露。
@Override
public Map
建立一个空Map,遍历edges,找出以target为终点的边edge;
weight=0时表示该边不存在,跳过;将edge.source->edge.weight加入Map,遍历结束后返回这个Map.
@Override
public Map
建立一个空Map,遍历edges,找出以source为起点的边edge;
weight=0时表示该边不存在,跳过;将edge.source->edge.weight加入Map,遍历结束后返回这个Map.
@Override
public String toString();
将所有边的toString连接在一起并输出。
由于这个类继承了GraphInstanceTest类,因此通用的方法不需再编写测试,只需要测试ConcreteVerticesGraph中特有的方法,其他的方法继承GraphInstanceTest已经实现的方法。
下面有关一些简单的方法的测试不给出具体的实现,在代码中各个测试类均有自己的测试策略,详见代码
@Test public void testgetname();
@Test public void testgetsoure();
@Test public void testgettarget();
@Test public void testaddandremovesource();
@Test public void testaddandremovetarget();
@Test public void testtostring();
利用EclEmma测试覆盖度
这里几乎覆盖所有可能情况,有些地方在如极为简单的get方法没有涉及到,其次还有一些由于调用时的封装无法涉及已经完成了较为全面的覆盖。
将两个实例类中的所有String类的参数替换为泛型的参数(声明、函数参数、返回值、rep)可以使用eclipse中的自动补全的功能按照提示修改所有的具体类为泛型。这里不再赘述,在上述问题实现的基础上做一些简单的变化就可以实现
public class ConcreteEdgesGraph implements Graph
public class ConcreteVerticesGraph implements Graph
修改Graph.empty为:
由于这个接口的两种实现方式的等价性因此在这个接口中的静态方法中返回任意一种方法即可这里以ConcreteEdgesGraph作为Graph默认的实例类,也可以用ConcreteVerticesGraph,二者是等价的。
涉及的函数有:
testemptyinput()//空文件输入
testsinglelineinput()//只有一行的文件输入
testmutilinesinput()//多行文件输入
testselectiveinput()//存在添加中间节点的选择问题
//rep:
private Graph corpusGraph = Graph.empty();
用于存储文本文件读入后按规则建立的有向图
//constructor:
public GraphPoet(File corpus) throws IOException
根据参数(文件),判断能否正常读入(否则抛出异常),并且调用poemGraph(corpus)方法创建好语料库(有向图)
主要思想为:用scanner类逐行读入文件,将每行按照“ ”进行分解,得到一个字符串数组,并将相邻的两个字符串组成一条边加入graph中既可以完成这个有向图的初始化建立。
//funcution:
public String poem(String input);
public String toString()//重写tostring方法
只需要调用图中实现的tostring方法即可。
这里在实现时提供了两个私有的辅助方法:
这个问题是应用自己更新语料库文件中的字符串和输入的字符串input。
我们选择在main class中实现
运行结果
语料库为:
Lab1
在实现P1中ConcreteEdgesGraph以及ConcreteVerticesGraph类 的基础上实现FriendshipGraph,即用import语句导入P1中实现的图结构通过基本操作实现FriendshipGraph中addVertex,addEdge和getDistance三个接口。在Lab1中的规约不变要求测试在修改之后的FriendshipGraph中仍可以正常通过。
public void addVertex(Person person)
遍历父类的vertices(),如果存在一个元素的name域与Person的name域相等,证明这个点已经存在,输出提示并结束程序,否则调用父类的add(person)将该点加入。
public void addEdge(Person p1, Person p2)
先调用父类的set(p1,p2,1),如果返回值为0证明这两个点之间不存在边,否则证明这两个点之间已经有边存在,输出提示
public int getDistance(Person p1, Person p2);
使用BFS算法求p1与p2之间的最短距离,BFS需要遍历邻居节点时调用父类接口的targets(p1)就可以获得p1的所有邻居节点。
主要步骤代码如下:
public Set getListofPersons();//采用防御式拷贝的方法返回person的set
public static void main(String Args[])
与Lab1相同
//rep:
private final String name;//该person的名字
//constructor:
//function:
没有特殊的一些方法,主要为一个chekrep检查不变量的方法,以及一个get方法和一个重写tostring方法。
注:这里没有将检查重名的行为交给FriendshipGraph中的addvertices方法而不是在person中因此,此时person的设计较为简单,仅仅存放一个person的名字即可。
由于实验指导书中已经给出一个简单的main()客户端的例子,这里main仅仅是一个简单的示例就不做过多的更改与扩展,采用指导书中所给出的的main()方法作为客户端,并会在test中对整个程序进行更加完备的测试。
在这里回答所提出的两个问题,并已经在程序中做出相应的扩展
1.运行的结果理论应该是:-1 -1 0 -1 运行代码所得结果与期望一致
2.我们修改addVertex函数,在对加入的person进行检查,如果这个person的name与之前我们加入过的点相同,并且不是同一个点重复加入,则判定为违反了Each person has a unique name,会打印提示信息并exit(-1)
相关功能已经实现并在test有相应的测试用例
在这个测试用例中主要实现对三个方法的测试。