2020年春季学期
计算机学院《软件构造》课程
Lab 2实验报告
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.1.5.2 Implement GraphPoet· 2
3.2 Re-implement the Social Network in Lab1· 2
3.3.2 主程序MyChessAndGoGame设计/实现方案··· 3
实验的主要目标是训练ADT的设计、规约、测试和使用OOP技术实现 ADT。
主要有下面几个方面:
- 针对给定的 应用问题,从描述中识别所需 ADT
- 设计 ADT 的规约并评估规约的质量
- 根据 ADT 的规划设计测试用例,并写出 testing strategy
- 实现 ADT 的泛型化
- 根据规约设计 ADT 的多种不同的实现;针对每种实现,设计 representition、
- rep invariant 和 abstraction function。
- 使用 OOP 实现 ADT 并判定表示不变性是否违反已经是否有 rep exposure
在实验一中已经配置好了
在这里给出你的GitHub Lab2仓库的URL地址(Lab2-学号)。
https://github.com/ComputerScienceHIT/Lab2-1180300604.git
请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
这个实验的主要目的是测试 ADT 的规约设计和 ADT 的多种不同的实现,
测试优先编程。
如何从GitHub获取该任务的代码、在本地创建git仓库、使用git管理本地开发。
使用git clone命令 到本地 移动到java项目中
-
-
- Problem 1: Test Graph <String>
-
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
Graph.empty()是返回一个空的 Graph<L>实现,测试在不同的 L 的情况下,只要 L 为 immutable 类型的数据类型就可以使用。使用Integer。
我使用边构造法来返回图
-
-
- Problem 2: Implement Graph <String>
-
将 Graph<L>分别基于边为主和点为主来实现图的存储和操作。
Abstraction function
Representation invariant
checkRep
toString
都需要重写或者完善。
-
-
-
- Implement ConcreteEdgesGraph
-
-
首先根据接口重写几个函数
public int set(L source, L target, int weight) {
if (weight < 0) {
throw new RuntimeException();
}
int r = 0;
Iterator<Edge<L>> iterator = edges.iterator();
while (iterator.hasNext()) {
Edge<L> edge = iterator.next();
if (edge.get_source().equals(source) && edge.get_target().equals(target)) {
r = edge.get_weight();
iterator.remove();
break;
}
}
if (weight > 0) {
edges.add(new Edge<L>(source, target, weight));
vertices.add(source);
vertices.add(target);
}
checkRep();
return r;
}
Set函数与remove函数是比较麻烦的,在写的过程中发现在操作中对list做更改会报错
所以改成对容器做操作
// Abstraction function:
// String represent vertex, class edge represent edges
// Representation invariant:
// in the set of vertex the vertex can not be null
// the edge can not be null
// Safety from rep exposure:
// vertices and edges are private
public void checkRep() {
for (L vertex : vertices) {
assert (vertex != null);
}
for (Edge<L> edge : edges) {
assert (edge.get_weight() > 0);
}
}
checkRep的检查贯穿几乎每个函数主要保证权值非负,节点非空。
private L source;
private L target;
private int weight=0;
// Abstraction function:
/*
* source is the start vertex of the edge, target is the end vertex of the edge
// Representation invariant:
/*
* the weight of the edge must be positive source and the target must not be the
* same one
*/
对于edge这个类的设计是用含有 三个属性 而外部是无法直接操作的
测试就是针对设计的模块,开始对异常检测不太熟练,后来发现,如果checkrep里面assert语句,可以用try catch语句检测加上fail语句检测AssertionError。同时还有一些
RuntimeException。
-
-
-
- Implement ConcreteVerticesGraph
-
-
这种方法和用edge的list表示一个图的区别主要是其属性包含一个节点,根据图论的知识,每个点都有出度和入度,有出去的边,有进来的边。所以很多操作都要改变其他顶点和该顶点的联系,要从list里面找到匹配的节点,这相比上一种实现有一些麻烦。
// Abstraction function:
/*
* list of vertex represent the nodes in the graph class vertex can be a node
* which contains out coming edge and out come edge
*/
// Representation invariant:
/*
* the weight of the edge should not <= 0 the source and the target should not
* be same the number of in coming edge must be same as the out coming edge
*/
// Safety from rep exposure:
/*
* vertices is private vertex can not be changed by outside
*/
该类里需要注意的同样是要权值为正,节点非空,source节点和target节点要不一样,和上一种实现基本一致,但相对数据结构的checkrep有一些区别。
private L node;
private Map<L, Integer> inedge = new HashMap<L, Integer>();
private Map<L, Integer> outedge = new HashMap<L, Integer>();
// Abstraction function:
// TODO
/*
* the node is the vertex inedge is in coming edge outedge is out coming edge if
* you find that a node's inedge is same as a node's out coming edge. You can
* say that one is the source and the other is the target.
*/
// Representation invariant:
// TODO
/*
* the weight of the edge must >= 0 the number of the in coming edges must be
* same the out edges. the node can not be node
*
*/
// Safety from rep exposure:
// TODO
// the inedges and outedges cannot be get outside
Node类里面涉及对于出边入边的一些操作,设置出边,设置入边,消去出边,消去入边
有几个模块的测试打包在了test测试函数里
-
-
- Problem 3: Implement generic Graph<L>
-
将已有的两个 Graph<String>的实现改为基于 Graph<L>的实现
注意将所有的实现全部改为泛型实现即可,然后在更改结束后,重新测试
ConcreteVerticesGraphTest 和 ConcreteEdgesGraphTest 两个测试,可以
测试通过。
-
-
-
- Implement Graph.empty()
-
-
1、graph.empty 返回一个 Graph 接口的具体实现即可,如
public static <L> Graph<L> empty() {
// throw new RuntimeException("not implemented");
return new ConcreteEdgesGraph<>(); }
2、测试 GraphStaticTest
使用不同的 L 泛型进行测试,基本数据类型均是 Immutable 类的,
。此处采用 Integer进行了测试,具体的测试结果如下
“诗意之旅”这个任务主要利用已有的图的接口,根据一段诗生成其对应的图,
然后根据这个生成的图再生成更多的诗
-
-
-
- Test GraphPoet
-
-
主要测试了在空文件 单行输入 多行输入的情况下是否能够通过测试
-
-
-
- Implement GraphPoet
-
-
public GraphPoet(File corpus)
根据输入改变对应map的权值
public String poem(String input)
根据输入,对应的map 添加桥接词
使用了自带的txt文件进行测试
请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
在这里给出你的项目的目录结构树状示意图。
重新实现 Lab1 中的 Social Network,利用在 P1 中已经写
好的 Graph<L>接口来实现
在这里简要概述你对该任务的理解。
使用 Graph<L>里面提供的函数 add,即可实现增加顶点的功能。只需
要在 addVertex 中在增加对于 distance 和 visited
实现 addEdge 函数
利用 Graph<L>里面提供的函数 set,可实现增加顶点
addEdge 中判断一下是否是源点和目的点是否相同即可
利用set(person1, person2,1)增加边。
实现 getDistance 函数
这里使用了队列,因为用搜索算法的时候,需要队列这种先进先出的特性
给出你的设计和实现思路/过程/结果。
由一个构造方法和一个getname组成
给出你的设计和实现思路/过程/结果。
-
-
- 客户端main()
-
重新利用 Lab1 中已有的 main 客户端即可
给出你的设计和实现思路/过程/结果。
FriendshipGraph graph = new FriendshipGraph();
Person rachel = new Person("Rachel");
Person ross = new Person("Ross");
Person ben = new Person("Ben");
Person kramer = new Person("Kramer");
Person KKK = new Person("KKK");
Person AAA = new Person("AAA");
graph.addVertex(rachel);
graph.addVertex(ross);
graph.addVertex(ben);
graph.addVertex(kramer);
graph.addVertex(KKK);
graph.addVertex(AAA);
graph.addEdge(ross, ben);
graph.addEdge(ben, ross);
graph.addEdge(ross,KKK );
graph.addEdge(KKK, ross);
graph.addEdge(KKK, AAA);
graph.addEdge(AAA, KKK);
graph.addEdge(rachel, ross);
graph.addEdge(ross, rachel);
assertEquals(-1,graph.getDistance(rachel,kramer));
assertEquals(3,graph.getDistance(rachel,AAA));
assertEquals(2,graph.getDistance(ben,KKK));
assertEquals(1,graph.getDistance(rachel,ross));
assertEquals(2,graph.getDistance(rachel,ben));
assertEquals(0,graph.getDistance(rachel,rachel));
看是否能够满足最基本的功能,在改写过后,进行测试,全部通过,其实还可以对空图测试、仅有顶点的图测试、无环图测试和有环图测试,时间有限还没来得及写测试。
给出你的设计和实现思路/过程/结果。
-
-
- 提交至Git仓库
-
使用git命令即可
Add commit push等等
如何通过Git提交当前版本到GitHub上你的Lab3仓库。
在这里给出你的项目的目录结构树状示意图。
-
-
- ADT设计/实现方案
-
设计了哪些ADT(接口、类),各自的rep和实现,各自的mutability/ immutability说明、AF、RI、safety from rep exposure。
整体来说,有一个主类,里面有一个主函数,用来控制控制台,控制台流程和要求里的基本一致,用户 选择一个游戏,输入玩家名字,然后给出一系列指令范例
程序会创建两个player类每个里面包含一些玩家特征,比较重要的是一个action类的list用来存储每个玩家的操作。
用户根据样例键入指令,主程序用split函数分解指令 swich语句根据不同操作指令做出动作,调用board类中的方法对棋盘上的棋子做出操作,board类会调用position中的方法。
在每一次的操作中,创建一个action类,将该类添到对应player的action类list里面。
Position
/*
* AF
* position represent the state of that point
* color is black or white
* type is "chess" or "go"
*
* RI
* x,y is in a range
* color is only two kinds
* type is only chess or go
* */
public class Position {
int x;
int y;
boolean visited;
String type;
boolean color;
Position(int x,int y,boolean color,String type)
{
this.x=x;
this.y=y;
this.color=color;
this.type=type;
}
首先是position 类 他表达了棋盘上的某一个坐标的状态,是否有子,什么颜色等等
Player
包含上述属性
安全性:全部通过方法访问属性
Action类
里面存储了action的许多方法
用来改变和访问类里的属性
比较重要的是get_position方法可以根据传入游戏类型输出不同格式的操作
在主函数中被迭代调用
Position
Board
-
-
- 主程序MyChessAndGoGame设计/实现方案
-
Go
Chess
- 用户输入 棋类名字 chess or go
- 根据不同棋类调用board类里的初始化函数初始化棋盘
- 给用户打印指令样例
- 用户输入指令
- 将字符串拆分存入数组,访问数组0位关键指令,同时使用switch 映射到board中的方法,对棋盘操作,同时记录下当前player的action,存入该player的action数组
- 输入end结束
- 询问是否需要打印步骤图
- 若要打印则分别先后打印player1 和player2的步骤
辅之以执行过程的截图,介绍主程序的设计和实现方案,特别是如何将用户在命令行输入的指令映射到各ADT的具体方法的执行。
-
-
- ADT和主程序的测试方案
-
分别单独测试board里的方法 move eat put remove
测试成功
加入部分主程序 在主程序运行过程中加以测试
介绍针对各ADT的各方法的测试方案和testing strategy。
介绍你如何对该应用进行测试用例的设计,以及具体的测试过程。
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
日期 | 时间段 | 计划任务 | 实际完成情况 |
3.28 | 20:00-1:00 | 完成P1 | 完全没有看懂……英语太差,到差不多12点才大致能够理解让做什么东西 |
4.1 | 20:00-24:00 | 完成P1 | 完成了P1的所有测试 |
4.2 | 20:00-1:00 | 完成P1 | 完成了以边为基础的构造方法 |
4.3 | 20:00-24:00 | 完成P1 | 完成了以点为基础的构造方法 |
4.4 | 20:00-24:00 | 完成P2 | 完成 |
4.5 | 20:00-1:00 | 完成P3 | 设计ADT 编写测试 |
4.9 | 20:00-1:00 | 完成P3 | 实现部分类 |
4.12 | 20:00-1:00 | 完成P3 | 实现部分类 |
遇到的难点 | 解决途径 |
不太能理解网站上的描述, 英语不太好,难以理解任务要求,不具备测试优先的思想,或者熟练的ADT设计习惯。 | 不断重复的阅读,理解要求,上网查阅资料,看学长写的报告 |
容器 队列 list用的不太好
| 看java书 |
以vertex为基础建立图的时候对于图的知识忘的很多了
| 翻看离散数学引论 发现出度入度 |
棋类游戏没啥思绪
| 多次阅读要求,拿草纸设计 |
|
|
|
|
实验设计的本身是很好的,但是自己java 和OOP技术比较欠缺,自己还需要去看书,学习java编程技术
- 面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?
- 使用泛型和不使用泛型的编程,对你来说有何差异?
- 在给出ADT的规约后就开始编写测试用例,优势是什么?你是否能够适应这种测试方式?
- P1设计的ADT在多个应用场景下使用,这种复用带来什么好处?
- P3要求你从0开始设计ADT并使用它们完成一个具体应用,你是否已适应从具体应用场景到ADT的“抽象映射”?相比起P1给出了ADT非常明确的rep和方法、ADT之间的逻辑关系,P3要求你自主设计这些内容,你的感受如何?
- 为ADT撰写specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后编程中坚持这么做?
- 关于本实验的工作量、难度、deadline。
- 《软件构造》课程进展到目前,你对该课程有何体会和建议?