本次实验训练抽象数据类型(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 并据此设计测试用例。
简要陈述你配置本次实验所需环境的过程,必要时可以给出屏幕截图。特别是要记录配置过程中遇到的问题和困难,以及如何解决的。
在 Eclipse IDE 中安装配置 EclEmma(一个用于统计 JUnit 测试用例的代码覆盖度的 plugin)直接从Eclipse Market下载安装即可。
在这里给出你的GitHub Lab2仓库的URL地址(Lab2-学号)。
https://github.com/ComputerScienceHIT/Lab2-1170301026.git
请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
分别在两个类ConcreteEdgesGraph,ConcreteVerticesGraph 实现Graph接口。
Graph接口要求实现add(添加新节点),set(添加新边),remove(移除节点),vertices(获得所有的节点集合),sources(target)获得以target为目标节点的边的起始节点,targes(source)获得以source为起始节点的边的目标节点。
Poet:假设存在一条由a到b的有向边,构造有向图,再给定一句子,如果句子中两个相邻单词在有向图中有一个中间单词,则将该单词插入到a与b中间,若存在多个中间单词,则插入权重最大的那个
自https://github.com/rainywang/Spring2019_HITCS_SC_Lab2/tree/master/P1 获得实验代码。
Git命令:
git init
git remote add origin [email protected]:ComputerScienceHIT/Lab2-1170301026.git
git pull origin master
git add .
git commit -m “init”
git push origin master
测试Graph需将empty改成如下:
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
Fileds | 作用 |
---|---|
private L source | 起始节点 |
private L target | 目标节点 |
private int weight | 边权值 |
Method | 作用 |
---|---|
getsource() | 返回有向边起始节点 |
gettarget() | 返回有向边目标节点 |
getweight() | 返回边权值 |
@Override toString() | 使用@Override注释toString以确保正确覆盖Object方法的toString方法 |
/**
* TODO specification Immutable. This class is internal to the rep of
* ConcreteEdgesGraph.
*
*
* PS2 instructions: the specification and implementation of this class is up to
* you.
*/
class Edge<L> {
// TODO fields
private final L source;
private final L target;
private final int weight;
// Abstraction function:
// AF(source,target,weight)=an edge contains the source vertex,the target vertex label,weight
// Representation invariant:
// weight no negative number
// Safety from rep exposure:
// All fields are private;
// source and target are Strings, so are guaranteed immutable;
// use final to guarantee weight is immutable;
// TODO constructor
public Edge(L s, L t, int w) {
this.source = s;
this.target = t;
this.weight = w;
checkRep();
}
// TODO checkRep
//Check the rep invariant is true
//***Warning:this does nothing unless you turn on assertion checking
// by passing -enableassertions to Java
private void checkRep() {
assert weight>=0;
}
// TODO methods
public L getsource() {
checkRep();
return source;
}
public L gettarget() {
checkRep();
return target;
}
public int getweight() {
checkRep();
return weight;
}
// TODO toString()
@Override
public String toString() {
String tmp = null;
tmp=source.toString()+target.toString()+Integer.toString(weight);
checkRep();
return tmp;
}
}
method | 作用 |
---|---|
add(L vertex) | 调用vertices.add,其返回结果为boolean且满足spec定义。 |
set(L source,L target,int weight) | 检查输入满足weight!=0,若满足则调用edges.add添加边并返回weight,若满足weight=0且该边存在于edges中 |
remove(L vertex) | 检查输入满足vertex存在vertices中,当存在时,删除该节点,同时删除所有与之相连的边 |
Set vertices | 返回vertices |
Sources(L target) | 遍历edges,调用gettarget方法,若找到与tareget相同的,则将该边加入map中 |
Targets(L source) | 遍历edges,调用getsource方法,若找到与source相同的,则将该边加入map中 |
@Override toString() |
/* Copyright (c) 2015-2016 MIT 6.005 course staff, all rights reserved.
* Redistribution of original or derived work requires permission of course staff.
*/
package src.P1.graph;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* An implementation of Graph.
*
*
* PS2 instructions: you MUST use the provided rep.
*/
public class ConcreteEdgesGraph<L> implements Graph<L> {
private final Set<L> vertices = new HashSet<>();
private final List<Edge<L>> edges = new ArrayList<>();
// Abstraction function:
// AF(vertices,edges)=a graph with vertices and edges
// Safety from rep exposure:
// All fields are private
// use final to guarantee immutable
@Override
public boolean add(L vertex) {
try {
if (!vertices.contains(vertex)) {
vertices.add(vertex);
return true;
} else
return false;
} catch (Exception e) {
throw new RuntimeException("not implemented");
}
}
@Override
public int set(L source, L target, int weight) {
try {
Edge<L> e = new Edge<L>(source, target, weight);
if (weight != 0) {
edges.add(e);
return weight;
} // if weight nozero,add edge and set weight
else if (weight == 0 && edges.contains(e)) {
edges.remove(e);
return 0;
} // if weight zero&the edge exists,
else
return 0;
} catch (Exception e) {
throw new RuntimeException("not implemented");
}
}
@Override
public boolean remove(L vertex) {
try {
if (vertices.contains(vertex)) {
for (Edge<L> item : edges) {
if (vertex == item.getsource() || vertex == item.gettarget()) {
edges.remove(item);
}
}
return vertices.remove(vertex);
} else
return false;
} catch (Exception e) {
throw new RuntimeException("not implemented");
}
}
@Override
public Set<L> vertices() {
try {
return vertices;
} catch (Exception e) {
throw new RuntimeException("not implemented");
}
}
@Override
public Map<L, Integer> sources(L target) {
try
{
Map <L,Integer>map=new HashMap<>();
for(Edge<L> item:edges)
{
if(target==item.gettarget())
{
map.put(item.getsource(),Integer.valueOf(item.getweight()));
}
}
return map;
}
catch(Exception e)
{
throw new RuntimeException("not implemented");
}
}
@Override
public Map<L, Integer> targets(L source) {
try
{
Map <L,Integer>map=new HashMap<>();
for(Edge<L> item:edges)
{
if(source==item.getsource())
{
map.put(item.gettarget(),Integer.valueOf(item.getweight()));
}
}
return map;
}
catch(Exception e)
{
throw new RuntimeException("not implemented");
}
}
// TODO toString()
@Override public String toString() {
String tmp = null;
tmp = getClass().getName() + '@' + Integer.toHexString(hashCode());
return tmp;
}
}
Field | 作用 |
---|---|
private L label | 节点名字 |
private Map |
所有以label为目标节点的边,<起始节点lable,边的权重>. |
private Map |
所有以label为其实节点的边,<目标节点label,边的权重> |
Interface | 作用 |
---|---|
setSource(L source,int weight) | 检查输入满足source!=null,weight>=0。当weight==0时,调用this.removeSource,当weight>0时,调用Map.put修改source并且记录初始值。 |
removeSource(L source) | 检查输入满足source!=null,调用sources.remove,并返回(不存在则返回0)。 |
setTarget(L target,int weight) | 检查输入满足target!=null,weight>=0,当weight=0时,调用this.removeTarget,当weight>0时,调用targets.put并返回(不存在则返回0)。 |
removeTarget(L target) | 检查满足target!=null。调用targets.remove(),返回。 |
remove(final L vertex) | 调用removeSource和removeTarget |
Method | 实现思路 |
---|---|
add(L vertex) | 检查输入满足vertex!=null。遍历点集vertices,若已存在vertex则返回false,否则调vertices.add并返回true |
set(L source,L target,int weight) | 遍历点集两次,分别检查vertices中是否存在source vertex和target vertex,若均不存在则调用vertices.add并调用source.setTarget和target.setSource设置边和权值, |
remove(L vertex) | 检查输入满足vertex!=null。如果输入节点不存在则返回false,否则,遍历vertices中的每一个节点调用v.remove在targes和sources中删除该节点, |
Set vertices(L vertex) | 遍历点集,找到Label与vertex相同的点并加入Setans中,最后返回ans |
Sources(L target) | 遍历点集.如果没有target则返回空集合,否则调用targetVertex.getSources(target) |
Targets(L source) | 遍历点集如果没有target则返回空集合,否则调用sourceVertex.getTargets(source) |
Field | 说明 |
---|---|
Private final Graph affinityGraph | 配置文件生成的Graph |
Private final List corpusWord | 文集包含的单词 |
用P1中实现的ConcreteEdgeGraph或者ConcreteVertexGraph实现FriendshipGraph
public class FriendshipGraph extends ConcreteEdgesGraph<Person> {
/**
* Add Vertex(Person)
*
* @param a person p
*/
public void addVertex(Person p) {
this.add(p);
}
/**
* Add Edge
*
* @param Person pa
* @param Person pb
* @param weight
*/
public void addEdge(Person pa, Person pb, int w) {
this.set(pa, pb, w);
}
/**
* Calculate the distance between two person
*
* @param a person pa
* @param a person pb
* @return the shortest distance between two person,return -1 if personA can't
* reach personB
*/
public int getDistance(Person pa, Person pb) {
if (pa == pb) {
return 0;
}
int dis = 1;
// BFS
Queue<Person> queue = new LinkedList<Person>();
// use LinkedList to implement queue
Map<Person, Integer> map = new HashMap<Person, Integer>();
queue.offer(pa);
while (!queue.isEmpty()) // queue not empty
{
Person p = queue.poll();// return top person
Map<Person, Integer> neighbour = this.targets(p);
Iterator entries = neighbour.entrySet().iterator();
// Traversing the map to find key person
while (entries.hasNext()) {
Map.Entry<Person, Integer> entry = (Map.Entry<Person, Integer>) entries.next();
Person key = entry.getKey();
Integer value = entry.getValue();
if (!map.containsKey(key)) {
map.put(key, value);
queue.offer(key);
if (key == pb) {
return map.get(key);
}
}
}
dis++;
}
return -1;
}
}
函数 | 实现思路 |
---|---|
addVertex(Person p) | 调用父类接口this.add(newPerson) |
addEdge(Person pa,Person pb) | 调用父类接口this.set(pa,pb,1) |
getDistance(Person pa,Person pb) | 使用BFS算法求得图中stPerson与edPerson之间的最短距离,BFS中需要遍历邻居节点,遍历map并调用父类接口的targets方法获得父类接口 |
与实验一中基本一致
直接采用实验一测试代码,认识的两人之间权值设置为1即可