欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

软件构造LAB2

程序员文章站 2022-03-10 14:49:26
...

1实验目标概述
本次实验训练抽象数据类型(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实验环境配置
在 Eclipse IDE 中安装配置 EclEmma(一个用于统计 JUnit 测试用例的代码覆盖度的 plugin)直接从Eclipse Market下载安装即可。
https://github.com/ComputerScienceHIT/Lab2-1180300721
实验过程
请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
2.1Poetic Walks
分别在两个类ConcreteEdgesGraph,ConcreteVerticesGraph 实现Graph接口。
Graph接口要求实现add(添加新节点),set(添加新边),remove(移除节点),vertices(获得所有的节点集合),sources(target)获得以target为目标节点的边的起始节点,targes(source)获得以source为起始节点的边的目标节点。
Poet:假设存在一条由a到b的有向边,构造有向图,再给定一句子,如果句子中两个相邻单词在有向图中有一个中间单词,则将该单词插入到a与b中间,若存在多个中间单词,则插入权重最大的那个
2.1.1Get the code and prepare Git repository
git init
git remote add origin [email protected]m:ComputerScienceHIT/Lab2-1170301026.git
git pull origin master
git add .
git commit -m “init”
git push origin master
2.1.2Problem 1: Test Graph
测试Graph需将empty改成如下:
Public Graph empty(){
return new ConcreteEdgesGraph();
}
2.1.3Problem 2: Implement Graph
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
2.1.3.1Implement ConcreteEdgesGraph
private L source 起始节点
private L target 目标节点
private int weight 边权值
getsource() 返回有向边起始节点
gettarget() 返回有向边目标节点
getweight() 返回边权值
/**

  • 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 {

    // TODO fields
    private L source;
    private L target;
    private int weight;
    // TODO constructor
    public Edge(L source, L target, int weight) {
    this.source = source;
    this.target = target;
    this.weight = weight;
    checkRep();
    }

    // TODO checkRep
    public void checkRep() {
    assert source != null;
    assert target != null;
    assert weight >= 0;
    }
    // TODO methods

    // TODO toString()

    public L getSource() {
    return source;
    }

    public L getTarget() {
    return target;
    }

    public int getWeight() {
    return weight;
    }

    @Override
    public String toString() { // 覆盖超类的toString方法
    String s = this.getSource() + “->” + this.getTarget() + “:” + this.getWeight();
    return s;
    }

2.1.3.2Implement ConcreteVerticesGraph
private int findVertex(L str) {
for (int i = 0; i < vertices.size(); i++) {
if (vertices.get(i).getString().equals(str)) {
return i;
}
}
return -1;
}

@Override
public int set(L source, L target, int weight) {
	assert weight >= 0;
	assert source != target;
	Vertex<L> vertexOfSource, vertexOfTarget;
	int index;

	if (vertices().contains(source)) {
		index = findVertex(source);
		vertexOfSource = vertices.get(index);
	} else { // 如果string对应的点不在list中,创建新的点并且添加进去
		vertexOfSource = new Vertex<>(source);
		vertices.add(vertexOfSource);
	}

	if (vertices().contains(target)) {
		index = findVertex(target);
		vertexOfTarget = vertices.get(index);
	} else {
		vertexOfTarget = new Vertex<>(target);
		vertices.add(vertexOfTarget);
	}

	final int a1 = vertexOfSource.setTarget(target, weight); 
	final int a2 = vertexOfTarget.setSource(source, weight);
	assert a1 == a2;
	checkRep();
	return a1; // 返回pre_weight
}

@Override
public boolean remove(L vertex) {
	assert vertex != null;
	if (!vertices().contains(vertex)) {
		return false;
	}
	for(Vertex<L> one :vertices) {   //删除点的所有sources和targets
		if(one.getOneSources().containsKey(vertex)) {
			one.getOneSources().remove(vertex);
		}
		if(one.getOneTargets().containsKey(vertex)) {
			one.getOneTargets().remove(vertex);
		}
	}
	final int index = findVertex(vertex);
	vertices.remove(index);  
	return true;
}

@Override
public Set<L> vertices() {
	Set<L> s = new HashSet<L>();
	for (Vertex<L> one : vertices) {
		s.add(one.getString());
	}
	return s;
}

@Override
public Map<L, Integer> sources(L target) {
	assert target != null;
	int index = findVertex(target);
	if (index < 0) {
		return Collections.emptyMap();
	} else {
		return vertices.get(index).getOneSources();
	}
}

@Override
public Map<L, Integer> targets(L source) {
	assert source != null;
	int index = findVertex(source);
	if (index < 0) {
		return Collections.emptyMap();
	} else {
		return vertices.get(index).getOneTargets();
	}
}

// TODO toString()
@Override
public String toString() {
	if (vertices.isEmpty()) {
		return "This is An Empty Graph";
	}
	String stringGraph = "";
	for (int i = 0; i < vertices.size(); i++) {
		if(vertices.get(i).toString() != "") {
		stringGraph = stringGraph.concat(vertices.get(i).toString()+"\n");
		}
	}
	return stringGraph;
}

}

2.1.4Problem 3: Implement generic Graph

class Vertex {

// TODO fields
private L str;
private Map<L, Integer> oneSources = new HashMap<L, Integer>();
private Map<L, Integer> oneTargets = new HashMap<L, Integer>();

// Abstraction function:
// 顶点类刻画图的关键要素
// 使用HashMap存取映射关系
// key为该点的source或target , value为该点的weight
//
// Representation invariant:
// 每个顶点的source或target不能是自身
// HashMap中的values必须不小于0
//
// Safety from rep exposure:
// 所有fields是private final
// String是imutable类型
// 防御性编程

// TODO constructor

// TODO checkRep

// TODO methods

// TODO toString()

/*构造函数*/
public Vertex(L str) {
	this.str = str;
}

/* 检查不变量,保证该点不会是自己的source或者target,weight不小于0*/
public void checkRep() {
	assert !oneSources.keySet().contains(this.str);
	assert !oneTargets.keySet().contains(this.str);
	for(Integer v1 : oneSources.values()) {
		assert v1 >=0;
	}
	for(Integer v2 : oneSources.values()) {
		assert v2 >=0;
	}
	
	
}

public L getString() {
	return this.str;
}

public Map<L, Integer> getOneSources() {
	return this.oneSources;
}

public Map<L, Integer> getOneTargets() {
	return this.oneTargets;
}

/**
 * 设置到该点的source,处理同ConcreteVerticesGraph中set方法
 * weight = 0 , source存在 :移除
 * weight > 0 , source存在 :更新 
 * weight > 0 , source不存在 :添加
 * 
 * @param source ,weight of an edge
 * @return pre_weight of pre_edge
 */
public int setSource(L source, int weight) {
	assert source != null;
	assert source != this.str; // 不重合
	assert weight >= 0;

	int pre_weight = 0;

	if (weight == 0 && this.oneSources.keySet().contains(source)) { // remove
		pre_weight = oneSources.remove(source);
	}
	if (weight > 0 && this.oneSources.keySet().contains(source)) { // update
		pre_weight = oneSources.put(source, weight);
	}
	if (weight > 0 && !this.oneSources.keySet().contains(source)) { // add
		oneSources.put(source, weight);
		pre_weight = 0;
	}
	checkRep();
	return pre_weight;
}

/**
 * 设置到该点的target,处理同ConcreteVerticesGraph中set方法
 * weight = 0 , target存在 :移除
 * weight > 0 , target存在 :更新 
 * weight > 0 , target不存在 :添加
 * 
 * @param target ,weight of an edge
 * @return pre_weight of pre_edge
 */
public int setTarget(L target, int weight) {
	assert target != null;
	assert target != this.str; // 不重合
	assert weight >= 0;

	int pre_weight = 0;

	if (weight == 0 && this.oneTargets.keySet().contains(target)) { // remove
		pre_weight = oneTargets.remove(target);
	}
	if (weight > 0 && this.oneTargets.keySet().contains(target)) { // update
		pre_weight = oneTargets.put(target, weight);
	}
	if (weight > 0 && !this.oneTargets.keySet().contains(target)) { // add
		oneTargets.put(target, weight);
		pre_weight = 0;
	}
	checkRep();
	return pre_weight;
}

@Override
public String toString() {
	String str = "";
	for (L s : this.oneTargets.keySet()) {
		str = str.concat(this.getString() + "->" + s + ":" + this.oneTargets.get(s));
	}
	return str;
}

}
2.1.4.1Make the implementations generic
将类及接口声明改为:
public class ConcreteVerticeGraph implements Graph{
private final List<Vertex> vertices =new ArrayList<>();
public class ConcreteEdgesGraph implements Graph {
private final Set vertices =new HashSet<>();
private final List<Edge> edges =new ArrayList<>();
2.1.4.2Implement Graph.empty()
使Graph.empty()能返回一个新的空实例。代码如下:
public static Graph empty() {
return new ConcreteEdgesGraph();
}
2.1.5Problem 4: Poetic walks

2.1.4.3Test GraphPoet
在基于预设的测试用例基础上,增加等价类划分的多种情况。
等价类划分:两个单词之间不存在连接词,两个单词之间只有一个连接词,两个单词之间有多个连接词。
此外还要注意句末的句号,测试当一个句子最后一个词是“桥”的一端。
2.1.4.4Impleme GraphPoet
1.表示不变量和检查不变量,该应用中的不变量是所有的点都不为空
2.用文件输入单词,String.split()分割为数组,通过String.toLowerCase()小写化。接下来构建图,相邻的单词加边。首先要在加边前通过Graph.add()加点,加边时要判断是否存在:由于Graph.set()能返回之前加的边的值,以此来判断是否存在,存在则在之前的值加一(之前的边的值保存为lastEdgeWeight)。
3.Poem(String input)当相邻两个单词任意一个不在之前创建的图里,则将后者单词加入即可(再加个空格)当存在时,由于Bridge长度只能为2,所以:分别求两个单词的sources和targets,将该Map转换为Set求交集;若交集为空,则无桥,若交集不空,则在交集中找最短的桥(可以在Map的value中查询weight)。

2.1.4.5Graph poetry slam
样例“This is a test of the Mugar Omni Theater sound system.”进行测试,测试成功

修改样例为“This is a the Mugar system Omni Theater sound system test of the.”,测试成功。该样例用于测试极端情况

2.1.5Before you’re done
请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
在这里给出你的项目的目录结构树状示意图。

2.2Re-implement the Social Network in Lab1
这部分任务就是用我们在2.1中写的ADT,把第一次实验中的FriendshipGraph重新实现一遍,图中的节点仍然是Person类型,所以泛型L一律为Person. 而对于已经写好的FriendshipGraph中的方法,要用2.1中的Graph ADT中的方法来实现它们。
2.2.1FriendshipGraph类
public class FriendshipGraph extends ConcreteEdgesGraph{
public void addVertex(Person p) {
if (this.vertices().contains§) { //防止点重复
System.out.println(“This name is existed!”);
}
else {
this.add§;
}
}

public void addEdge(Person p1, Person p2) {
	if(!this.targets(p1).containsKey(p2)) { //防止边重复
		this.set(p1, p2, 1);
	}
}

public int getDistance(Person p1, Person p2) {
	Person now = p1 ;
	int distance = 0;   
	Queue<Person> queue = new LinkedList<Person>();
	ArrayList<Person> visited = new ArrayList<Person>();
	if(p1 == p2) {
		return distance;
	}
	queue.add(now);
	visited.add(now);
	while (!queue.isEmpty()) {
		now = queue.poll();   //取出队首
		distance ++;
		for(Person friend : this.targets(now).keySet()){
			if( friend == p2) {    //找到了即返回当前的距离
				return distance;
			}
			if(!visited.contains(friend)) {
				queue.add(friend);
				visited.add(friend);
			}
		}
	}
	return -1;   //找不到说明P1和P2没关系
}

public static void main(String[] args) throws Exception {
	FriendshipGraph graph = new FriendshipGraph();
	Person rachel = new Person("Rachel");
	Person ross = new Person("Ross");
	Person ben = new Person("Ben");
	Person kramer = new Person("Kramer");
		
	ArrayList<Person> list = new ArrayList<Person>();
	list.add(rachel);
	list.add(ross);
	list.add(ben);		
	list.add(kramer);
	for(int i=0; i <list.size();i++) {
		for(int j=i+1 ; j < list.size();j++) {
			if(list.get(i).nameSameWith(list.get(j).getName())) {
				System.out.println("Wrong name:"+list.get(i).getName());
				throw new Exception("The name is repeated!");
			}
		}
	}
	
	graph.addVertex(rachel);
	graph.addVertex(ross);
	graph.addVertex(ben);
	graph.addVertex(kramer);
	graph.addEdge(rachel, ross);
	graph.addEdge(ross, rachel);
	graph.addEdge(ross, ben);
	graph.addEdge(ben, ross);
	System.out.println(graph.getDistance(rachel, ross));
	//should print 1
	System.out.println(graph.getDistance(rachel, ben));
	//should print 2
	System.out.println(graph.getDistance(rachel, rachel));
	//should print 0
	System.out.println(graph.getDistance(rachel, kramer));
	//should print -1
}

}

2.2.2Person类
public class Person {
private String name;
public Person (String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public boolean nameSameWith(String name) { //确定姓名是否重复
if(this.name.equals(name)) {
return true;
}
else
return false;
}

2.2.3客户端main()
public class FriendshipGraphTest {
private static final FriendshipGraph graph = new FriendshipGraph();
private static final Person rachel = new Person(“Rachel”);
private static final Person ross = new Person(“Ross”);
private static final Person ben = new Person(“Ben”);
private static final Person kramer = new Person(“Kramer”);
private static final Person jack = new Person(“Jack”);
private static final Person paul = new Person(“Paul”);
private static final Person trump = new Person(“Trump”);

/**
 * Tests that assertions are enabled.
 */
@Test(expected = AssertionError.class)
public void testAssertionsEnabled() {
	assert false;
}

/**
 * Tests addVertex.
 */
@Test
public void testaddVertex() {
	graph.addVertex(jack);
	graph.addVertex(paul);
	graph.addVertex(trump);
	
    assertTrue("expected inclusion relationship", graph.vertices().contains(jack));
    assertTrue("expected inclusion relationship", graph.vertices().contains(paul));
    assertTrue("expected inclusion relationship", graph.vertices().contains(trump));
    
}


/**
 * Tests addEdge.
 */
@Test
public void testaddEdge() {
	graph.addEdge(rachel, ross);
	graph.addEdge(ross, rachel);
	graph.addEdge(ross, ben);
	graph.addEdge(ben, ross);
	
    assertTrue("expected connection", graph.targets(rachel).containsKey(ross));
    assertTrue("expected connection", graph.targets(ross).containsKey(rachel));
    assertTrue("expected connection", graph.targets(ross).containsKey(ben));
    assertTrue("expected connection", graph.targets(ben).containsKey(ross));
    assertFalse("expected unconnection", graph.targets(rachel).containsKey(kramer));
}

/**
 * Tests getDistance.
 */
@Test
public void getDistanceTest() {
	graph.addVertex(rachel);
	graph.addVertex(ross);
	graph.addVertex(ben);
	graph.addVertex(kramer);
	graph.addEdge(rachel, ross);
	graph.addEdge(ross, rachel);
	graph.addEdge(ross, ben);
	graph.addEdge(ben, ross);
	
	assertEquals(1, graph.getDistance(rachel, ross));
	assertEquals(2, graph.getDistance(rachel, ben));
	assertEquals(0, graph.getDistance(rachel, rachel));
	assertEquals(-1, graph.getDistance(rachel, kramer));
}

@Test
public void testPerson() {
	final Person rachel2 = new Person("Rachel"); 
	assertEquals("Expected proper name","Rachel",rachel.getName());
	assertTrue("Expected the same name",rachel.nameSameWith(rachel2.getName()));
	assertFalse("Expected not the same name",rachel.nameSameWith(ross.getName()));
}

}
2.2.4测试用例
根据题目中的社交网络图:
分别测试:
Rachel和Ross距离是1,Rachel和Ben距离是2
Rachel和Rachel距离是0
Rachel和Kramer距离是-1

2.2.5提交至Git仓库

2.3Playing Chess
2.3.1ADT设计/实现方案

1,Position类
位置信息
属性:int横坐标x,纵坐标y;
构造器以很纵坐标为参数,包含横纵坐标
主要方法:
public boolean equal(Position p2)方法断给定位置是否与参数位置相同
public int getX() 方法进行防御性拷贝,返回该位置x坐标
public int getY()方法进行防御性拷贝,返回该位置y坐标

2,Piece类
棋子类
属性:position p棋子的位置、string namestring 棋子名称、player ownerplayer 所属玩家
构造器参数:名称 位置 所属玩家
主要方法:
public Position getPosition() 进行防御型拷贝,返回棋子位置
public void setPosition(Position pos) 设置棋子位置

3,Player 类
玩家类
属性:String nameString 玩家姓名,private int tag;玩家标号,private Set playerPieces 玩家所拥有的棋子集合,
主要方法:
public Piece getPiece(Position position) 返回给定位置上的棋子
public int pieceCounter() 返回玩家拥有的棋子数
……

4,Board类
棋盘类
属性:int nx,ny 棋盘大小,String gametypeString 游戏种类,int[][] occupied=new int[nx][ny] 棋盘被占用情况。
主要方法:
public void init():根据游戏种类初始化棋盘,围棋初始为空,象棋初始在特定位置白方棋子
public boolean setBoard(int x,int y,int statue):设置棋盘某一位置被占用的状态
public int getCondition(Position position):查询棋盘被占用情况
public boolean isLegle(Position position):判断位置是否合法
5,Action类
活动类
属性:无
主要方法:
public static boolean putPiece:在特定位置下子
public static boolean removePiece:提子
public static Player winner:获得胜利玩家
public static boolean movePiece:移动棋子操作
public static boolean eatPiece:吃子操作

6,Game类
属性:
String gameString:游戏类别;
Player player1,player2:玩家一号二号;
构造器:
游戏名称,玩家一号,玩家二号
主要方法:
public void initgame():初始化游戏(主要是针对象棋初始化棋子)

2.3.2主程序MyChessAndGoGame设计/实现方案

设置计数器在一定的操作时加一模2使得两个玩家轮流操作。
按照要求选择完游戏类型输入玩家名称后,开始游戏。
1,放子操作针对围棋,检查输入合法性,将新生成坐标棋子添加到玩家的棋子集合中,并改变棋盘状态即可
2,移动棋子,检查输入合法性,检查现在位置棋子状态和目标位置状态,目标位置为空且现位置为本人棋子,改写棋子状态,棋盘状态,操作完成。
3,提子,检查输入合法性,合法则从检查棋盘状态,从玩家的棋子集合中删除该棋子,改写棋盘状态
4,吃子,检查输入合法性,检查现在位置棋子状态和目标位置状态,目标位置为对方棋子且现位置为本人棋子,改写棋子状态,从对方棋子集合中删除该棋子改写棋盘状态,操作完成
5,查询某位置占用情况,检查输入合法性,根据输入位置直接检查棋盘状态,根据棋盘状态返回值判断该位置被哪个棋手占用
6,获得玩家棋子数,调用玩家中的piececonter方法,直接得到棋子个数,输出
7,跳过,改写计数器,写一个玩家操作
2.3.3ADT和主程序的测试方案
模拟玩家进行操作,检查个数函数调用的返回值。采用assert断言。

其中chessgame经过实际操作验证为正确,不再进行测试。

,1,public void positionTest():对其中的添加返回等操作数值模拟
,2,public void playerTest():生成一个玩家,在玩家内对getTag(),getname()输入具体值进行测试,对.addPieces,pieceCounter,getPiece,remove返回值真假进行测试
3,public void pieceTest():生成piece,对其中的getposition,setposition模拟数值,对返回真假进行验证
4,public void boardTest():生成board,对其中每一条函数进行测试
5,public void gameTest(),对是否能正常生成初始化进行测试
6,public void actionTest():生成一个完整的游戏,对每一步操作的结果进行真假测试