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上方的菜单栏中,找到help
搜索软件并install
在这里给出你的GitHub Lab2仓库的URL地址(Lab2-学号)。
3 实验过程
请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1 Poetic Walks
先写出相应的测试文件,再分别用点和边实现一个图,Graph为接口,在ConcretEdgesGraph和ConcreteVertexGraph中实现接口中定义的各个函数,在poet中读入给定文本,相邻两个词之间构成一条有向边,从而构成一棵树,再输入给定的句子,若相邻两个词之间有一个词,则插入,有多个,则插入权值最大的。
3.1.1 Get the code and prepare Git repository
如何从GitHub获取该任务的代码、在本地创建git仓库、使用git管理本地开发。
从https://github.com/rainywang/Spring2019_HITCS_SC_Lab2/tree/master/P1中下载,创建相应的文件夹,在文件夹中单击鼠标右键,单击Git Bush Here,输入git remote 命令绑定代码仓库,上传时git add . git commit –m git push
3.1.2 Problem 1: Test Graph
思路及过程:在GraphInstanceTest中实现各个接口的测试,测试中要创建新的Graph,用emptyInstance()创建而不是根据某一特定实现创建。这样,在以后的测试中,只需用不同实现构建emptyInstance即可完成不同的测试。
结果:
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
3.1.3 Problem 2: Implement Graph
通过点和边分别实现图。
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
3.1.3.1 Implement ConcreteEdgesGraph
思路及过程:
首先,写Edge类,Edge有三个属性:start,end,value。由于Edge是不可变的,所以不能有Setter,只能有Getter。分别实现三个属性的Getter。
由边实现的图,有两个属性,一个set vertices存储图的所以顶点,一个List 存储所有边。由于List 和Set 是可变数据类型,在向外提供这两个数据时要copy一份再返回。
add方法:往集合vertices中add,先判断是否存在,若已经存在,则返回false。
set方法:如果weight<0或者起点终点相等,返回0。若weight=0,则寻找相应的边,删除,返回原weight。若weight>0,则寻找边重置权值,返回原weight,若没有这条边,返回0.
remove方法:如果该点存在,则删除该点和与之相关的边,返回TRUE,否则返回FALSE。
vertices方法:copy一份点集,返回。
sources方法:遍历集合中每一个点,在List中遍历每一条边,找到其source点和相应权值,组成map,返回。
target方法:遍历集合中每一个点,在List中遍历每一条边,找到其target点和相应权值,组成map,返回。
tostring方法:输出每一个边的起点,终点,权值。
结果:
3.1.3.2 Implement ConcreteVerticesGraph
思路及过程:首先,写Vertex类,每个点有三个属性,name,targetOfv(目标点及其权值),sourceOfv(源点及其权值)。
ConcreteVerticesGraph有一个属性,点的List。
add方法:判断是否存在,若存在,返回FALSE,否则,List.add。返回TRUE。
set方法:若权值小于0,返回0,权值等于0,找到相应的边,重置权值,返回之前的weight,若找不到,则返回0。
remove方法:若不存在,返回FALSE,否则,List.remove。返回TRUE。
vertices方法:将点的List转换为set,返回。
targets方法:返回点的属性map:targetOfv。
sources方法:返回点的属性sourceOfv。
tostring方法:返回点的源点集合,目标点集合,及其相应边的权值所构成的String。
结果:
3.1.4 Problem 3: Implement generic Graph
思路及过程:将所有Vertex和Edge改为Vertex Edge
接口的实现改为public class ConcreteVerticesGraph implements Graph
public class ConcreteEdgesGraph implements Graph
再把相应的String 改为L。
结果:
3.1.4.1 Make the implementations generic
思路及过程:将所有Vertex和Edge改为Vertex Edge
接口的实现改为public class ConcreteVerticesGraph implements Graph
public class ConcreteEdgesGraph implements Graph
再把相应的String 改为L。
结果:
3.1.4.2 Implement Graph.empty()
思路及过程:默认用Edge实现
结果:
3.1.5 Problem 4: Poetic walks
先读入文本构建graph,再输入一段文字,若两个词之间存在一个词,就添加到其中,若存在多个,选择权值最高的。
3.1.5.1 Test GraphPoet
思路及过程:分别对构建graph,扩展诗句两个功能测试
结果:
3.1.5.2 Implement GraphPoet
思路及过程:相邻两个词构成一条边,如果存在,就将原来的权值+1;否则,设置权值为1的新边。
结果:
3.1.5.3 Graph poetry slam
思路及过程:将前一个顶点的targetOfv和后一个顶点的sourceOfv作交集,选择权值最大的点添加。
结果:
3.1.6 Before you’re done
上传命令:
$ git add.
$ git commit –m “have been finished”
$ git push
目录结构:
3.2 Re-implement the Social Network in Lab1
在lab1的基础上,依据P1依据实现的graph,实现FriendshipGraph,把L改为Person。实现addvertex,addEdge,getDistance的功能。
3.2.1 FriendshipGraph类
思路及过程:FriendshipGraph类继承自ConcreteVerticesGraph。加点调用add(person),加边调用set(person1,person2)。计算距离用广度搜索,与之前不同的是,目标点集合用targets(source)获取。
结果:
3.2.2 Person类
思路及过程:Person有唯一一个属性,name。方法:getName,Override equals。
需要重写equals方法,以便graph中的各个方法能正确运行。
结果:
3.2.3 客户端main()
思路及过程:和lab1中的main相同
结果:
3.2.4 测试用例
思路及过程:测试addvertex时,考虑已经存在的情况和不存在的情况,getdistance考虑距离无限大,两个点相同的特殊情况。
结果:
3.2.5 提交至Git仓库
上传命令:
$ git add.
$ git commit –m “have been finished”
$ git push
目录结构:
3.3 Playing Chess
3.3.1 ADT设计/实现方案
设计了哪些ADT(接口、类),各自的rep和实现,各自的mutability/ immutability说明、AF、RI、safety from rep exposure。
必要时请使用UML class diagram(请自学)描述你设计的各ADT间的关系。
3.Player
String history用以存储下棋过程
addHistory添加下棋过程
4. Board
int type 0代表chess,1代表go
xlimit和ylimit用以存储边界
构造函数Board(int,int)需要输入类型和size
containPiece用以查询某个棋子是否存在
containxy用以查询某个位置是否有棋子
getnumber用以查询某个玩家的棋子数
getPiecexy用以查找某个位置上的棋子
outofboard用来判定是否越界
removePiece删除某个位置上棋子
setPiecexy往某个位置添加棋子
5. Action
属性:两个Player,一个Board
方法:
Getter和Setter
putin放子
remove提子
eat吃子
move移动子(均为Boolean类型,如果出现异常,会抛出异常并作相应处理)
getnum获取某个玩家棋子数
6. Game
属性:action动作,两个玩家Player,类型type,棋盘gameBoard
方法:
构造函数Game(int,Player,Player)只需输入type和两个玩家,自动创建棋盘,并由gameBoard和Player组成action。
Getter和Setter
initGame,初始化棋盘,如果是chess,则将棋子初始化到棋盘上,否则只初始化棋盘。
吃子,移动,提子,放子四个操作均调用action的方法。出现异常并捕获异常,出现异常提示和异常路径。不会影响主程序继续执行。
3.3.2 主程序MyChessAndGoGame设计/实现方案
game是该类唯一的属性,main是程序的主函数,menu打印菜单,otherPlayer转换玩家。
main函数:
默认第一步选择初始化,用户输入0和1代表chess或者go,再输入两个玩家姓名,从而创建game,并调用game.initGame进行初始化。
随后由第一个玩家开始选择,若在chess模式选择了放子或者提子,则提示只能选择4,5;go模式下会提示只能选择1,2。出现不合理操作,或者选择了7,8,9都会让当前玩家继续操作,否则轮到另一个玩家。end结束,打印双方走棋历史。在下棋过程中,会提示越界,提自己的子,违规操作等异常,但不会终止程序。
辅之以执行过程的截图,介绍主程序的设计和实现方案,特别是如何将用户在命令行输入的指令映射到各ADT的具体方法的执行。
运行截图:
3.3.3 ADT和主程序的测试方案
主要测试了Board类中的越界,获取某个位置的棋子,移除棋子,放入棋子,获取某玩家棋子数目的方法,着重测试位置不合法,操作不合法。
然后测试了Action中的操作