目录
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.2 Re-implement the Social Network in Lab1 2
3.3.2 主程序MyChessAndGoGame设计/实现方案 3
实验目标概述
本次实验训练抽象数据类型(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
实验环境配置
本次实验的总体配置和上次实验相同,这里不再赘述。唯一需要增添的插件就是代码覆盖工具,即EclEmma。
一开始我在网站上搜索EckEmma的配置过程,但是发现在官网上下载似乎需要FQ,这令我而苦恼,而后我又在的eclipse商店查找相关信息,结果发现也无法下载。 稍微思考了一下我又去搜了一下,原来现在的eclipse直接装的就有这个工具,因此实验环境就不用再配置了。
在这里给出你的GitHub Lab2仓库的URL地址(Lab2-学号)。
https://github.com/ComputerScienceHIT/Lab2-1180500313.git
实验过程
请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
Poetic Walks
在这里简要概述你对该任务的理解。
Get the code and prepare Git repository
如何从GitHub获取该任务的代码、在本地创建git仓库、使用git管理本地开发。
git clone https://github.com/rainywang/Spring2020_HITCS_SC_Lab2.git
在本地获取该任务代码只需输入上述代码即可。
git init
git remote add origin git@github.com:ComputerScienceHIT/Lab2-1180500313.git
用上面的语句初始化本地仓库并做第一次提交的测试,之后用git管理本地开发时进行类似的操作即可。
Problem 1: Test Graph <String>
这里我们只需要测试Graph<String>中的empty()方法即可。补全Graph中的empty()。
public static Graph<String> empty() {
//throw new RuntimeException("not implemented");
return new ConcreteEdgesGraph();
此外由于测试中使用了Graph.empty().vertices()方法,我们将其补全,返回HashSet()。
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
为了实现graph接口我们总体的想法就是测试优先,首先写出测试,然后根据规约进行设计。本实现方式是基于边的。
//图的边是有向正权的,图中没有复边(两条方向和连接点均相同的边
// vertices和edges是private final的,信息对外界是隐藏的
其中右侧为用边实现一个有向正权图的所有方法和变量,左侧为实现一个完整的图所使用的方法和变量。其中比较重要的方法如:
public int set(L source, L target, int weight) {
if (e.getSource().equals(source) && e.getTarget().equals(target)) {
currentweight = e.getWeight();
edges.add(new Edge<L>(source, target, weight));
if (e.getSource().equals(source) && e.getTarget().equals(target)) {
这个地方实际上实现的是设置一条有起点、有终点、带权值的边的方法。我们通过输入的权值来更新目前储存的权值,便于返回操作得到的状态(成功更改,未找到目标顶点),实现方式是迭代器的查找。
这里达到了94.6%,至于没能完全测试的原因是涉及到异常的测试,这一部分还没学习,因此就未进行测试
//图的边是有向正权的,图中没有复边(两条方向和连接点均相同的边
// vertices是private final的,信息对外界是隐藏的
其中右侧为用顶点角度实现一个有向正权图的所有方法和变量,左侧为实现一个完整的图所使用的方法和变量。其中比较重要的方法如:
public int setSource(L source, int weight) {
throw new RuntimeException("边权不为负");
now_Weight = this.removeSource(source);
now_Weight = this.source_vertex.put(source, weight);
if(now_Weight==null) now_Weight=0;
这里达到了93.0%,至于没能完全测试的原因是涉及到异常的测试,这一部分还没学习,因此就未进行测试.
将上面涉及到具体对象类型的地方改为泛型实现,可以采用eclipse中的重构功能。
public static <L> Graph<L> empty() {
Graph<L> graph = new ConcreteEdgesGraph<L>();
//graph是private final的,信息对外界是隐藏的
public String poem(String input) {
StringBuilder SB = new StringBuilder();
List<String> list = new ArrayList<String>(Arrays.asList(input.split(" ")));
Map<String, Integer> sourceMap = new HashMap<>();
Map<String, Integer> targetMap = new HashMap<>();
for (int i = 0; i < list.size() - 1; i++) {
SB.append(list.get(i)).append(" ");
String sourceString = list.get(i).toLowerCase();
String targetString = list.get(i + 1).toLowerCase();
targetMap = graph.targets(sourceString);
sourceMap = graph.sources(targetString);
for (String string : targetMap.keySet()) {
if (sourceMap.containsKey(string) && sourceMap.get(string) + targetMap.get(string) > maxWeight) {
maxWeight = sourceMap.get(string) + targetMap.get(string);
SB.append(list.get(list.size() - 1));
我们的方法覆盖度为97%,这也进一步印证了我们代码的正确性。
请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
我们将原来用邻接表表现社交关系换为了用有向图来表现,有向图是以边和顶点构成的。并且有向图的边是正权的,而不是局限于lab1的只有一种权。
public int getDistance(Person Person1, Person Person2) {
Queue<Person> queue = new LinkedList<>();
Map<Person, Integer> distantMap = new HashMap<>();
Person topPerson = queue.poll();
int nowDis = distantMap.get(topPerson);
Set<Person> friendList = people.targets(topPerson).keySet();
if (!distantMap.containsKey(ps)) {
distantMap.put(ps, nowDis + 1);
return distantMap.get(Person2);
这里使用了队列和深度优先搜索来实现计算两人之间的距离。具体思路就是:用diatantmap保存其他人到自己的距离,然后入队自己,将朋友加入队列,不是person2就加入队列,是就返回最终距离。
public Person(String nameString) {
if (personlist.contains(nameString)) {
System.out.println("出现了重复的名字");
public class FriendshipGraphTest {
@Test(expected = AssertionError.class)
public void testAssertionsEnabled() {
public void testsamePerson() {
FriendshipGraph graph = new FriendshipGraph();
assertFalse(graph.getPeople().vertices().contains(f));
FriendshipGraph graph = new FriendshipGraph();
assertEquals("expected distance", 1, graph.getDistance(a, b));
public void testsaddEdgenotexist() {
FriendshipGraph graph = new FriendshipGraph();
assertEquals("expected distance", 1, graph.getDistance(a, b));
public void testFriendshipGraph() {
FriendshipGraph graph = new FriendshipGraph();
assertEquals("expected distance", 1, graph.getDistance(a, b));
assertEquals("expected distance", 1, graph.getDistance(b, c));
assertEquals("expected distance", 1, graph.getDistance(c, d));
assertEquals("expected distance", 2, graph.getDistance(a, c));
assertEquals("expected distance", 2, graph.getDistance(b, d));
assertEquals("expected distance", 3, graph.getDistance(a, d));
assertEquals("expected distance", -1, graph.getDistance(b, a));
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
private final ArrayList<String> historys = new ArrayList<>();
private int historycounter = 0;
// historys是private final的,信息对外界是隐藏的
private final Set<Piece> boardSet = new HashSet<>();
// boardSet是private final的,信息对外界是隐藏的
//String ,int都是不可变的数据类型,防止了表示的泄露
//gameAction,PlayerA,PlayerB都是private,final的
辅之以执行过程的截图,介绍主程序的设计和实现方案,特别是如何将用户在命令行输入的指令映射到各ADT行。
goGameMenu | 输出围棋的菜单 |
cheseGameMenu | 输出国际象棋菜单 |
gameMain | 游戏尚未明确类型时的客户端函数 |
goGame | 执行围棋操作 |
cheseGame | 执行国际象棋的函数 |
main | 声明一个MyChessAndGoGame实例,调用gameMain方法,启动游戏。 |
在游戏函数中,针对落子,提子,吃子一类操作,都由客户端读取输入,分解用户输入,检查输入是否合法:(这里以落子为例,其他类似)
介绍针对各ADT的各方法的测试方案和testing strategy。
主程序主要使用手动测试的方法,针对参数越界,控制权不符合要求,输入参数不足等情况手动测试。
主要测试putPiece,removePiece,move,eat几个重点操作。
Piece piece1 = new Piece("AAA", 1);
Piece piece2 = new Piece("AAA", 1);
assertTrue(B.putPiece(piece1));
assertFalse(B.putPiece(piece1));
assertFalse(B.putPiece(piece2));
public void testremovePiece() {
Piece piece1 = new Piece("AAA", 1);
Position pa = new Position(1, 1);
Position pb = new Position(2, 2);
assertTrue(B.removePiece(pa));
assertFalse(B.removePiece(pb));
Board B = game.getGameBoard();
Player paPlayer = new Player();
Position pa = new Position(0, 0);
Position pb = new Position(2, 2);
Position pc = new Position(3, 3);
Position pd = new Position(999, 999);
assertTrue(B.move(paPlayer, pa, pb));
assertTrue(B.move(paPlayer, pb, pc));
assertFalse(B.move(paPlayer, pa, pb));
assertFalse(B.move(paPlayer, pd, pb));
Board B = game.getGameBoard();
Player paPlayer = new Player();
Position pa = new Position(0, 0);
Position pb = new Position(2, 2);
Position pc = new Position(3, 3);
Position pd = new Position(999, 999);
assertTrue(B.move(paPlayer, pa, pb));
assertTrue(B.move(paPlayer, pb, pc));
assertFalse(B.move(paPlayer, pa, pb));
assertFalse(B.move(paPlayer, pd, pb));
public void testAssertionsEnabled() {
public Game gametest =new Game("b");
public void testgettersetter() {
gametest.setNames("pl1", "pl2");
assertEquals("pl1", gametest.getPlayerA().getPlayerName());
assertEquals("pl2", gametest.getPlayerB().getPlayerName());
Piece testPiece1=new Piece("black",0);
Piece testPiece2=new Piece("white",1);
Piece testPiece3=new Piece("white",1);
Position P1 =new Position(1,1);
Position P2 =new Position(2,1);
Position P3 =new Position(3,1);
gametest.addnewPiece(gametest.getPlayerA(), testPiece1, P1);
gametest.addnewPiece(gametest.getPlayerB(), testPiece2, P2);
gametest.addnewPiece(gametest.getPlayerB(), testPiece3, P3);
assertTrue(gametest.getGameBoard().getBoardSet().contains(testPiece1));
assertTrue(gametest.getGameBoard().getBoardSet().contains(testPiece2));
assertTrue(gametest.getGameBoard().getBoardSet().contains(testPiece3));
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
日期 | 时间段 | 计划任务 | 实际完成情况 |
2020-03-12 | 18:30-21:50 | 完成P1问题的边实现 | 未完成 |
2020-03-16 | 19:00-24:00 | 完成P1问题的边实现 | 完成 |
2020-03-18 | 18:30-22:00 | 完成P1问题的顶点实现 | 完成 |
2020-03-18 | 22:00-23:50 | 完成P1 | 未完成 |
2020-03-19 | 18:30-24:00 | 完成P1 | 完成 |
2020-03-24 | 17:30-21:00 | 完成P2 | 完成 |
2020-03-28 | 09:00-22:00 | 完成P3 | 未完成 |
2020-04-04 | 13:00-21:00 | 完成P3 | 完成 |
2020-04-05 | 13:00-24:00 | 更新代码,完成实验报告 | 完成 |
遇到的难点 | 解决途径 |
忘记怎么书写规约,AF,RI等 | 查看PPT |
Eclipse不会安装UML生成工具
| 查看博客 |
|
在类的总体设计能力上仍有欠缺。协调这些类的能力也有欠缺。我得到的经验是:首先要明白自己要设计什么,写出测试,然后根据目标为导向进行程序设计会快得多。