2020年春季学期计算机学院《软件构造》课程Lab 2实验报告
- 1实验目标概述
- 2实验环境配置
- 3实验过程
- 3.1Poetic Walks
- 3.2 Re-implement the Social Network in Lab1
- 3.3 Playing Chess
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下载安装即可。
在这里给出你的GitHub Lab2仓库的URL地址(Lab2-学号)。
https://github.com/ComputerScienceHIT/Lab2-1183710129
3实验过程
3.1Poetic Walks
该任务主要是实验一个图的模块,并基于此使用。
(1)完善Graph接口类,并运用泛型的思想,将String拓展为泛型L类;
(2)实现Graph类的方法:add、set、remove、vertices、sources、targets;
(3)利用实现的Graph类,应用图的思想,实现GraphPoet类,如果输入的文本的两个单词之间存在桥接词,则插入该桥接词;若存在多个单一桥接词,则选取边权重较大者。
3.1.1 Get the code and prepare Git repository从https://github.com/rainywang/Spring2020_HITCS_SC_Lab2/tree/master/P1获取初始代码
Git命令:
git init
git remote add origin git@github.com:ComputerScienceHIT/Lab2-1183710129.git pull origin master
git add .
git commit -m “init”
git push origin master
3.1.2 Problem 1: Test Graph
这部分主要是针对Graph设计测试策略,编写测试用例主要利用等价类划分的思想进行测试,测试策略如下:
编写覆盖以上条件的测试用例。
3.1.3 Problem 2: Implement Graph
3.1.3.1 Implement ConcreteEdgesGraph
1.实现Edge
Fileds | 作用 |
---|---|
private final L source; | 起始节点 |
private final L target; | 目标节点 |
private final int weight; | 边权值 |
Method | 作用 |
---|---|
public L getsource() | 返回有向边起始节点 |
public L gettarget() | 返回有向边目标节点 |
public int getweight()) | 返回边权值 |
public String toString() | 使用@Override注释toString以确保正确覆盖Object方法的toString方法 |
2.实现ConcreteEdgeGraph
Method | 作用 |
---|---|
public boolean add(L vertex) | 调用vertices.add,其返回结果为boolean且满足spec定义。 |
public int set(L source, L target, int weight) | 输入source,target,weight,确定一条有向边。具体做法:如weight!=0,移去可能已经存在的边,然后加入新的边,如weight=0.寻找可能已经存在边并删除。 |
public boolean remove(L vertex) | 从vertices中删去,传入的参数vertex点,遍历edges,寻找是否有边的起点或者是终点是该vertex,删去。注意在使用迭代器遍历时要使用iterator.remove方法保证安全。 |
public Set vertices() | 返回vertices集合 |
public Map<L, Integer> sources(L target) | 根据传入的target参数寻找以targe为终点的边。返回一个键值对为(点,权重)的map。 |
public Map<L, Integer> targets(L source) | 根据传入的source参数寻找以source为起点的边。实现同上。 |
关于AF,RI和rep exposure:
3.1.3.2 Implement ConcreteVerticesGraph
1.实现Vertex
Field | 作用 |
---|---|
private final L name | 节点名字 |
private final Map<L, Integer>sources = new HashMap<>() | 所有以name为目标节点的边,<起始节点name,边的权重>. |
private final Map<L, Integer>targets = new HashMap<>() | 所有以name为起始节点的边,<目标节点name,边的权重> |
Method | 作用 |
---|---|
public L getname() | 返回该节点的name |
public Map<L, Integer> getsources() | 根据传入的target参数寻找以targe为终点的边。返回一个键值对为(点,权重)的map。 |
public Map<L, Integer> gettargets() | 根据传入的source参数寻找以source为起点的边。实现同上。 |
public boolean removesource(L source) | 检查输入满足source!=null,调用sources.remove,并返回(不存在则返回false)。 |
public boolean removetarget(L target) | 检查满足target!=null。调用targets.remove(),返回。 |
public boolean setsource(L source, int weight) | 检查输入满足source!=null,weight>=0。当weight==0时,调用this.removeSource,当weight>0时,调用Map.put修改source并且记录初始值。 |
public boolean settarget(L target, int weight) | 检查输入满足target!=null,weight>=0,当weight=0时,调用this.removeTarget,当weight>0时,调用targets.put并返回(不存在则返回false)。 |
2.实现ConcreteVerticeGraph
Method | 作用 |
---|---|
public boolean add(L vertex) | 检查输入满足vertex!=null。遍历点集vertices,若已存在vertex则返回false,否则调vertices.add并返回true |
public int set(L source, L target, int weight) | 遍历点集,分别检查vertices中是否存在source vertex和target vertex,若均不存在则调用vertices.add并调用source.setTarget和target.setSource设置边和权值, |
public boolean remove(L vertex) | 检查输入满足vertex!=null。如果输入节点不存在则返回false,否则,遍历vertices中的每一个节点调用v.remove在targes和sources中删除该节点。 |
public Set vertices() | 遍历点集,找到Label与vertex相同的点并加入Set中,最后返回Set。 |
public Map<L, Integer> sources(L target) | 遍历点集.如果没有target则返回空集合,否则调用targetVertex.getSources(target) |
public Map<L, Integer> targets(L source) | 遍历点集如果没有target则返回空集合,否则调用sourceVertex.getTargets(source) |
关于AF,RI和rep exposure:
3.1.4 Problem 3: Implement generic Graph
3.1.4.1 Make the implementations generic
将两个实例类中的所有String类的参数替换为泛型的参数(声明、函数参数、返回值、rep)
3.1.4.2 Implement Graph.empty()
修改Graph.empty为:
这里以ConcreteEdgesGraph作为Graph默认的实例类,也可以用ConcreteVerticesGraph,二者是等价的
使用Double类进行泛型测试,测试图为:
测试结果为:
3.1.5 Problem 4: Poetic walks
3.1.5.1 Test GraphPoet
关于测试策略:
具体测试:
3.1.5.2 Implement GraphPoet
public GraphPoet(File corpus) | 打开文件,读取文件输入,识别序列,构建图结构。具体:利用BufferedReader.readLine方法读取全部输入后用string.split以空格划分,保存在数组中,随后每次取相邻元素,在图中新增边。 |
---|---|
public String poem(String input) | 还是利用相同方法分割输入字符串,声明一个StringBuilder保存返回结果。每次读取一个词,然后以当前词为source,下一个词为target,在graph中寻找符合此条件的边,记录权值,结束后选择权值最大的,利用StringBuilder. Append方法,将节点名字加入字符串。 |
public String toString() | 调用ConcreteEdgesGraph的toString方法,输出图结构 |
关于AF,RI和rep exposure:
3.1.5.3 Graph poetry slam
原始数据是网上的一篇作文。
3.1.6 Before you’re done
请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
git add .
git commit -m “xxx”
git push -u origin master
在这里给出你的项目的目录结构树状示意图。
3.2 Re-implement the Social Network in Lab1
继承P1中ConcreteEdgesGraph或者ConcreteVerticesGraph类 实现FriendshipGraph,通过基本操作实现FriendshipGraph中addVertex,addEdge和getDistance三个接口,要求不能修改父类rep。
3.2.1 FriendshipGraph类
将以前的邻接表结构改为新的有向图结构。
声明:
作为存放person点的有向图,这里采用ConcreteVerticesGraph这一实现,更加符合我们对关注的是每一个“人”的抽象。
Method | 作用 |
---|---|
public boolean addVertex(Person e) | 遍历父类的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 Setpersons() | copy一份vertices,存储图中所有的顶点,即关系网络中的所有人。 |
public static void main(String[] args) | 与Lab1相同 |
3.2.2 Person类
由于继承了ConcreteVerticesGraph,所以可以调用父类的rep和function,因此Person类就不需要过多的修饰。
3.2.3 客户端main()
Lab1实验手册给出。
3.2.4 测试用例
与Lab1相同。
3.2.5 提交至Git仓库
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
git add .
git commit -m “xxx”
git push -u origin master
在这里给出你的项目的目录结构树状示意图。
3.3 Playing Chess
3.3.1 ADT设计/实现方案
1.Position(mutable)
Method | 作用: |
---|---|
public boolean set(int x, int y) | 修改位置的横纵坐标 |
public int getX()public int getY() | 得到横纵坐标 |
2.Piece(mutable)
Method | 作用: |
---|---|
public String Name() | 得到棋子的名字 |
public Color color() | 得到棋子的颜色 |
public Piece clone() | 返回一个与该棋子具有相同属性的不同棋子 |
3.Player(mutable)
Method | 作用: |
---|---|
public String Name() | 得到玩家的名字 |
public Color color() | 得到玩家的颜色 |
public boolean addpiece(Piece e) | 该玩家获得一枚棋子 |
public void addstep(String step) | 添加一步到该玩家的走棋历史中 |
public List History() | 得到该玩家的走棋历史 |
public Set pieces() | 得到该玩家所拥有的棋子 |
4.Board(mutable)
Method | 作用: |
---|---|
public int size() | 得到棋盘的大小 |
public Piece[][] pieces() | 得到棋盘上棋子的分布 |
public void set(Piece piece, Position point) | 向该棋盘上指定位置上放置一枚棋子 |
public Piece get(Position s) | 获得棋盘上某位置的状态 |
5.Actions(interface)
Method | 作用: |
---|---|
public boolean CreatePlayer(String name) | 创造一个不重复的新玩家 |
public boolean Detach(Position point) | 移除棋盘上指定位置上的棋子 |
public Board board(); | 得到棋盘的状态 |
public boolean Islegal_Position(Position point); | 判断一个坐标是否在该棋盘上 |
public void Skip(); | 放弃此回合 |
public Piece Query(Position s); | 查询某个位置的占用情况(空闲,或者被某一方的什么棋子所占用) |
public int Calculate(Player player); | 计算玩家在棋盘上的棋子总数 |
public Player GetPlayer1(); | 返回玩家1的对象 |
public Player GetPlayer2(); | 返回玩家2的对象 |
6.InternationalChess(实例类)
提前初始化国际象棋的棋子
Method | 作用: |
---|---|
public boolean Move(Position s, Position t) | 将s上的棋子移动到t上 |
private boolean Rule(Piece E, Position s, Position t) | 判断国际象棋各个兵种行棋的步骤是否符合规则 |
7.ChineseGo(实例类)
Method | 作用: |
---|---|
public boolean Place(Position s) | 将s上的棋子移动到t上 |
3.3.2 主程序MyChessAndGoGame设计/实现方案辅之以执行过程的截图,介绍主程序的设计和实现方案,特别是如何将用户在命令行输入的指令映射到各ADT的具体方法的执行。
(1)主函数:
运行过程:
(2)以国际象棋为例
(3)根据不同选项进行不同操作的函数,查询和计算不会轮换对手
(4)程序结束,打印双方走棋历史
3.3.3 ADT和主程序的测试方案
1.ActionsInstanceTest
Test | 策略: |
---|---|
@Test(expected=AssertionError.class) | make sure assertions are enabled with VM argument: -ea |
public void test_CreatePlayer_GetPlayer() | 有重复玩家、无重复玩家 |
public void test_Detach() | 有无棋子、坐标在棋盘内、坐标不在棋盘内 |
2.BoardTest
Test | 策略: |
---|---|
public void test() | 检查棋盘大小是否相等、在棋盘上放置棋子,看能否得到正确的棋子 |
3.PieceTest
Test | 策略: |
---|---|
public void test() | 判断名字、颜色是否相等,该棋子和它的克隆不相等 |
4.PlayerTest
Test | 策略: |
---|---|
public void test() | 检查玩家名字,颜色是否相等,添加棋子重复和不重复,能否得到正确的走棋历史 |
5.PositionTest
Test | 策略: |
---|---|
public void test() | 能够得到正确的横纵坐标,检查toString |