实验目标:
本次实验训练抽象数据类型(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 并据此设计测试用例。
实验任务:
Get the code and prepare Git repository
git仓库代码:
git init 创建仓库
git add * 添加仓库文件
git commit -m “lab2” 添加信息
git remote add origin url 添加远程库信息
git push -u origin master 上传内容
Poetic Walks
Test Graph
本问要完成测试,只需修改graph中的构造函数就可以完成测试。
改为生成子类即可。
Implement Graph
Implement ConcreteEdgesGraph
本小问在于实现ConcreteEdgesGraph类。
题目给了我们点的集合和边的列表,储存各自信息,我们只要实现接口函数即可。
先实现边类
有以下三个内部属性:头节点、尾节点、权重,组成。
之后,主类有如下六个函数特别需要实现:
1、添加点
2、设置边
3、从点集合中删去点
4、获取点集合
5、获取某点连接的下一节点的Map
6、获取某点连接的上一节点的Map
Implement ConcreteVerticesGraph
本小问要求实现ConcreteVerticesGraph类,同样是实现接口函数,只不过这次只给了边的列表。
本次的节点类内部属性如下:
由一个标志量和两个分别用于查找上一节点和下一节点的Map组成。
函数具体实现与上一问大同小异,略去。
Implement generic Graph
本问要求将String类型的输入参数改为泛型编程。
首先,在Graph类下,类声明处为Graph:
将Graph类中的需要修改为泛型的String类型变量都改为L泛型即可完成本问。
Make the implementations generic
同上,要求改为泛型编程。
将ConcreteEdgesGraph类的声明改为ConcreteEdgesGraph,
ConcreteVerticesGraph类也进行如上操作。
之后,将两个类中的需要修改为泛型的String类型变量都改为L泛型即可完成本问。
Implement Graph.empty()
实现Graph.empty()函数
代码如下:
这里生成ConcreteEdgesGraph类,以进行之后的test和GraphPoet。
Poetic walks
Test GraphPoet
本问要求写出测试。
我这里的测试采用mit网页上提供的测试样例,该样例可以检验编写的GraphPoet的大部分功能。
Implement GraphPoet
本问要求实现GraphPoet。
有以下两个函数需要实现:
1、读取模板
使用FileInputStream进行文件读取。
实现的思路是读取所有文本内容后,将文本分割成单词,把单词使用P1中实现的Graph来保存下来,weight就是两个单词连接的次数。
2、按模板补全诗
实现的思路是读取输入的内容,进行分割,按顺序与模板中的词进行以下匹配:若前后两个输入的词都在模板中,且夹着某个词,就将所夹的词填入内容中;若这样的词有多个,则选择weight最大的一个填入。按照这个思路,即可进行诗的补全。
Graph poetry slam
本小问实现main函数。
main函数与mit提供的模板。
2、Re-implement the Social Network in Lab1
本任务是要使用P1中实现的Graph来重写Lab1的任务。
FriendshipGraph类
具体实现的代码如下:
FriendshipGraph类此处继承ConcreteEdgesGraph类,泛型此处应用为Person类。
大部分需要的功能在ConcreteEdgesGraph类中都实现了,我们只要添加getDistance的函数即可。
这里使用BFS,搜索到第一个与目标相同的对象时走的距离即是Distance,返回即可。
若未找到就返回-1以提示错误。
Person类
Person类里我只给了两个属性:
String name;
int dis;
name用于辨识不同的Person。
dis方便BFS搜索。
客户端main()
main函数使用Lab1中案例。
将添加点的函数改为add,添加边的函数改为set,权重设置为1即可。
测试用例
此处我的测试用例选用的是Lab1的测试用例,与上一小问的情况相似,只要修改添加点的函数和添加边的函数即可。
测试用例覆盖了除了main函数外的绝大部分的代码。
3、Playing Chess
ADT设计/实现方案
Ⅰ、接口:
此处我设计了两个接口:
Piece 和 Action。
Piece接口负责棋子的操作,有以下三个:放置到目标位置,获取潜在的有效位置,获取操控者。具体的传入参数和返回值信息见以下代码:
(补充说明:放置到目标位置需要获取潜在有效位置来确认目标位置是否合法)
Action接口负责Player的操作,有以下四个:放置新棋子(用于围棋)、移动棋子(用于国际象棋)、查询目标位置信息、输出双方棋子数。具体的传入参数和返回值见以下代码。
设计了哪些ADT(接口、类),各自的rep和实现,各自的mutability/ immutability说明、AF、RI、safety from rep exposure。
必要时请使用UML class diagram(请自学)描述你设计的各ADT间的关系。
Ⅱ、类
一、Position,位置类
内部存储位置坐标信息(横坐标、纵坐标值)与该位置棋子信息以及所属棋盘信息。
二、Board,棋盘类
内部存储Position的二维数组和棋盘规模信息。
三、设计两个棋子的大类,实现Piece接口。
1、GoPiece,为围棋的棋子类
围棋判断是否是潜在目标位置的方法是:遍历一遍棋盘上所有的位置,若某个位置没有被占用,则是潜在目标位置。
下子之后调用checkRep函数,查询所下位置上下左右四个位置,若四个位置有对方棋子被包围,则提子。
提子函数是将棋子信息从对应Position中抹除,对应Position回到未被占用状态。
2、ChessPiece,为国际象棋的棋子类
该类为不同类型的棋子的父类,只实现下子函数,而没有实现获取潜在的有效位置的函数。具体的潜在位置获取的实现交给子类。
该类负责实现下子、吃子函数,吃子函数的实现方法是目标抹除目标位置的棋子信息,改为自己。
子类:
①Pawn: 兵,直走斜吃的三个位置是潜在目标位置。
②Rook: 车,直走不越子的所有位置是潜在位置。
③Knight: 马,日字格的至多八个位置是潜在位置。
④Bishop: 相/主教, 斜走不越子的所有位置是潜在位置。
⑤Queen: 后, 直走斜走不越子的所有位置是潜在位置。
⑥King: 王,直走斜走一格内违背占有的位置是潜在位置。
如此就实现了国际象棋的所有棋子类。
四、Player类实现Action接口:
1、放置新棋子:生成一个新的围棋棋子,调用其放置函数放置到目标位置。
2、移动棋子:选取初始位置的棋子,调用其放置函数放置到目标位置。
3、查询目标位置信息:输出目标位置坐标、占有棋子类型、操控者信息。
4、输出双方棋子数:遍历棋盘上所有位置,统计双方棋子个数并输出。
五、MyChessAndGoGame类
用于实现控制台交互,并实现main函数。
主程序MyChessAndGoGame设计/实现方案
MyChessAndGoGame是游戏主程序类。
在创建该类时,就同时创建了两位玩家、国际象棋或围棋的棋盘,并若是国际象棋,还将按规则摆放上棋子。即,创建该类时就完成所有的初始化设置。
类函数,角色操纵函数。
传入参数为某一个玩家以及当前的游戏模式(国际象棋/围棋),根据相应规则,输出提示信息,并读取玩家输入的命令。
读取命令之后,分割命令,根据第一个单词,来确定具体是哪个操作。并辅以后续参数,完成操作。
main函数只负责创建该类,之后运行时,不断切换角色执行以上操作函数,实现游戏的进行,直到一方提出end,结束游戏。
具体操作截图:
ADT和主程序的测试方案
介绍针对各ADT的各方法的测试方案和testing strategy。
介绍你如何对该应用进行测试用例的设计,以及具体的测试过程。
测试方案:
我的测试方案是在后台分别模拟一次国际象棋和围棋游戏。
考虑各类场景,主要是对棋子下子的测试:
包括:正常下子到空位置,下子后提子(围棋),下子后吃子(国际象棋),企图下子超出棋盘,企图下子到棋盘中的非法位置等情况;以及玩家的下子、移动、查点、查数四个操作。
最后的测试覆盖了除了负责游戏进行的MyChessAndGoGame类以外的绝大部分内容。