Lab 2

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 并据此设计测试用例。
    针对以下所有四个任务,请为每个你设计和实现的 ADT 撰写mutability/immutability 说明、AF、RI、safety from rep exposure。给出各 ADT 中每个方法的spec。为每个 ADT 编写测试用例,并写明 testing strategy。

2 实验环境配置

操作系统:macOS Mojave 10.14.4
硬件环境:CPU:Intel Core i7 @3.1GHz
RAM:16GB LPDDR3
开发、测试、运行环境:IntelliJ IDEA Ultimate 2019.1
GitHub Lab_2 URL:unavailable

3 实验过程

3.1 Poetic Walks

总体上,主要是要完成一个关于有向图的ADT并在此基础上完成相关的应用并加以测试。通过之前完成的ADT可以通过输入的源素材建立一个语料库,通过这个语料库,对输入的原文本进行扫描、查找和替换,在此基础上进行润色加工处理。

3.1.1 Get the code and prepare Git repository

从提供的网站上下载源代码并按照要求格式创建Project。在指定文件夹用git init命令初始化仓库并连接到Lab2,根据流程建立仓库。

3.1.2 Problem 1: Test Graph

根据每个方法(操作)的功能依此建立相应的测试,以考察到各个功能是否正确实现并排除各种特殊情况。
经过测试,成功实现了80%以上的覆盖。具体的细节测试受时间限制没有深入测试,但其主要功能没有问题。

3.1.3 Problem 2: Implement Graph

事实上根据要求,首先需要实现一个ConcreteEdgesGraphConcreteVerticesGraph的String版本,然后再实现一个泛型版本。而两者是非常相似的,后期只需稍加修改便可实现泛型。

3.1.3.1 Implement ConcreteEdgesGraph

需要建立一个用边描述并可对其进行直接操作的图。其中包括了各种方法还有一个Edge类。首先描述``Edge类。它是图中的一条边,包含了source点,target点和权值3个信息,在此基础上定义了设置权值等方法。
对于add操作,直接在边的点集中添加点即可。
对于set操作,用一个辅助方法indexOfEdge,它可以找到边中符合条件的边,并返回列表中的位置,如果没有满足条件的点则返回一个负数,表明没有满足条件的点。首先判断权重,如果大于0,那么考虑indexOfEdge,如果返回负数,就直接创造两个点,并建立一条边,否则直接在相应边上修改权值,并返回之前的权值。如果权值为0,那么就移除该边,返回之前权值。
对于remove操作,可以使用函数接口Predicate提取出满足条件的点和边,分别删除:对于点来说,只需删除该点即可;对于边,则挑选出所有原点或目标点包含该点的边,将其删除。最后使用断言确认是否删删除成功。
对于vertices,直接返回点集即可。
对于source,可以直接寻找满足条件的target点是target的点。
对于target,可以直接寻找满足条件的source点是source的点。
对于toString,其返回方式比较随意。如果为空就返回一个提示字符串,否则返回一个原点到目标点的Label
对于Edge中的构造则无需赘述,并且其equalshashCode并没有用到。

3.1.3.2 Implement ConcreteVerticesGraph

需要建立一个用点描述并可对其进行直接操作的图。其中包括了各种方法还有一个Vertex类。首先描述Vertex类。它是图中的一个点,包含了source边MaptargetMapLabel共3个信息,在此基础上定义了设置权值等方法。
对于add操作,若判断已存在于列表中则返回false,否则建立新的实例加入到List中即可。
对于set操作,用一个辅助方法indexOfVertices,它可以找到点中符合条件的点,并返回列表中的位置,如果没有满足条件的点则返回一个负数,表明没有满足条件的点。判断sourcetarget点,若不存在则直接创建,存在则进行下一步操作。利用Vertex类中的辅助方法,可以直接对目标点的属性进行操作,并且并返回之前该边的权值。如果权值为0,那么就移除该点及对应边,返回之前权值。
对于remove操作,如果不存在所要删除点则返回false。否则再列表中遍历点,以此操作完成移除。
对于vertices,直接返回所有点的Label即可。
对于source,如果不存在该点则返回空,否则直接返回该点的sourceMap即可。
对于target,如果不存在该点则返回空,否则直接返回该点的targetMap即可。
对于toString,其返回方式比较随意。使用streamfilter完成操作,格式与之前类似。
对于Vertex中的构造则无需赘述,但是ConcreteVerticesGraph中难度较高的便是Vertex中的方法,上面的各种操作底层实现源于Vertex中的基本操作。下面介绍Vertex中的基本操作。
addSource:该方法可以在sourceMap中添加一个(source, weight);
addTarget:同理;
setSource:若权值为0,则移除source点,否则用addSource添加source点和权值,若source已存在并且权值不同,则用新的source和权值进行替换。最终返回之前的权值。
setTarget:同上;
remove:直接移除source中的该点和target中的该点。
toString:返回一个描述该点sourcetarget点的字符串。

3.1.3.3 Problem 3: Implement generic Graph<L>

Graph接口,其实现上面已经完成。

3.1.3.4 Make the implementations generic

非常简单,只需将String替换为泛型的参数L。方法同上。

3.1.3.5 Implement Graph.empty()

只需利用ConcreteEdgesGraph建立一个新的graph实例即可。

3.1.4 Problem 4: Poetic walks

进入应用阶段。也印证了标题的作用,通过之前完成的ADT可以通过输入的源素材建立一个语料库,通过这个语料库,对输入的原文本进行扫描、查找和替换,在此基础上进行润色加工处理。

3.1.4.1 Test GraphPoet

测试了单个单词,一行句子和多行句子的情况。
只需根据语料库替换的条件,通过输入相应条件的等待句子,使其行为相符即可。

3.1.4.2 Graph poetry slam

使用列表corpusWords储存输入的原素材单词,并用Graph结构储存语料库。
提取单词的过程中将单词均转换为小写形式便于判断。
将建立的词库转换为图:使用generateAffinityGraph方法,循环提取紧接着的单词,用set进行输入即可,权值为先前的权值+1;
最后是生成新诗的阶段:先将输入的句子分割为单个单词,遍历句子,提取每个单词的target和下一个的单词的source,使用filter在两者中取交集并转换为List即得到了bridge。如果bridge非空,则在bridge中用随机数随意选取一个单词插入到原来的语句相应位置。最后返回到String类型。

3.1.5 Before you’re done

在这里给出你的项目的目录结构树状示意图。
项目目录(非上交版本)
建立并初始化本地仓库后,使用git add -A命令选择目标文件,并git commit -m alpha上传,最后git push到GitHub。
通过测试,覆盖率基本达到了预期标准。

3.2 Re-implement the Social Network in Lab1

使用之前建立的Graph ADT重构之前Lab的P3部分,并尽可能利用原有代码。
对于getDistance由于储存结构的改变使用了全新算法,其他基本不变,由于使用了已有ADT,实现更加简洁。

3.2.1 FriendshipGraph类

主体结构与Lab1完全一致,只不过Lab1使用了邻接矩阵作为基本结构。
使用了nameList储存name,用以区分是否同名,若同名则退出。
主要介绍三个基本操作。
对于addVertex,首先判断是否满足条件(重名),若满足则直接在graphnameList直接添加即可。
对于addEdge,直接add两个人名和权值,权值设为1。
对于getDistance,使用了类似于广度优先搜索的算法。先将所有点的distance设为0,每次遍历当前点的target集合,利用流和过滤器,该集合只包含已经visited过的Person,并标记为当前点距离+1,设为已访问,并放入队列。每次出队点继续按照上述规则遍历。这样每个Person都包含了距离源点的距离,利用当前点距离计算下一个点的距离,直到找到目标点,如果所有点都被遍历而未找到则返回-1。

3.2.2 Person

与Lab1的基本一致。
为了计算距离,新增加了visiteddistance,用于储存当前点是否被访问过和到源点距离。

3.2.3 客户端main()

与Lab1的完全一致。

3.2.4 测试用例

与Lab1的完全一致。
由于主函数无需测试,已经达到了100%覆盖。

3.2.5 提交至Git仓库

项目目录(非上交版本)
建立并初始化本地仓库后,使用git add -A命令选择目标文件,并git commit -m alpha上传,最后git push到GitHub。

3.3 Playing Chess

3.3.1 ADT设计/实现方案

设计了一个Action接口。所用类包括BoardGameMyChessAndGoGamePiecePlayerPosition
其中Board包括构造器和各种重要方法,描述了棋盘尺寸、名称及种类。主要方法有:显示棋盘(状态)、生成棋子(ChessGo),检查棋子是否可以移动(判断各种条件)。
Game类,描述了游戏名称、种类。
Piece类,继承了Position类。主要描述棋子性质,包括所属玩家名称,是否在棋盘上,棋子名称及代码(便于操作和识别)。
Player类,继承了Action,描述了玩家名称,并实现了Action的具体方法,包括选择棋子和移动棋子。
Position,记录棋子当前位置。

3.3.2 主程序MyChessAndGoGame设计/实现方案

辅之以执行过程的截图,介绍主程序的设计和实现方案,特别是如何将用户在命令行输入的指令映射到各ADT的具体方法的执行。
主要为驱动程序。首先创建玩家,输入姓名,明根据输入0或1来选择游戏类别(国际象棋或围棋),接下来设置棋盘属性,无需输入并进入游戏环节。
对于国际象棋,需要输入棋子种类和代码,并输入指定位置,可以自动判断是否可以移动并且吃子,输出反馈信息和当前棋盘状态。
对于围棋,只需要输入坐标即可,可以自动判断是否可以移动,输出反馈信息和当前状态。
由于是命令行式设计,没有GUI,比较简陋,操作不方便,但是功能是相通的。围棋由于不清楚规则,没有判断胜负的功能。

3.3.3 ADT和主程序的测试方案

介绍针对各ADT的各方法的测试方案和testing strategy。
介绍你如何对该应用进行测试用例的设计,以及具体的测试过程。
首先建立两个Game类分别为ChessGo,建立两个玩家Billy和Van,分别建立棋子对象。
测试国际象棋是否存在可用棋子。
测试国际象棋指定棋子是否可以移动。
测试国际象棋游戏结束条件判定(一方棋子被吃光)。
测试围棋是否存在可用棋子。
测试围棋指定棋子是否可以移动。
主要功能都已测试,未测试到的实际上是暂时用不到的构造方法和主函数,没有任何影响。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值