关于一场Lab2的噩梦

软件构造Lab2之噩梦开始了

  • P1
    • 3.1 Poetic Walks
      • 3.1.1 Get the code and prepare Git repository
      • 3.1.2 Problem 1: Test Graph
      • 3.1.3 Problem 2: Implement Graph
        • 3.1.3.1 Implement ConcreteEdgesGraph
        • 3.1.3.2 Implement ConcreteVerticesGraph
      • 3.1.4 Problem 3: Implement generic Graph
        • 3.1.4.1 Make the implementations generic
        • 3.1.4.2 Implement Graph.empty()
      • 3.1.5 Problem 4: Poetic walks
        • 3.1.5.1 Test GraphPoet
        • 3.1.5.2 Implement GraphPoet
        • 3.1.5.3 Graph poetry slam
      • 3.1.6 Before you’re done
  • P2
    • 3.2 Re-implement the Social Network in Lab1
      • 3.2.1 FriendshipGraph类
      • 3.2.2 Person类
      • 3.2.3 客户端main()
      • 3.2.4 测试用例
      • 3.2.5 提交至Git仓库
  • P3
    • 3.3 Playing Chess
      • 3.3.1 ADT设计/实现方案
      • 3.3.2 主程序MyChessAndGoGame设计/实现方案
      • 3.3.3 ADT和主程序的测试方案
  • 后话

P1

3.1 Poetic Walks

该实验主要是对抽象数据类型的练习,达到测试和实现的目的。

3.1.1 Get the code and prepare Git repository

如何从GitHub获取该任务的代码、在本地创建git仓库、使用git管理本地开发。

3.1.2 Problem 1: Test Graph

上述的测试方法,大致都是下面这种情况,手动添加一些点,一些边,然后调用,主要采用assertEquals的方法,进行测试就行了。

对于toString,我们这里通过测试他的边的情况,对其进行测试,只要他输出的边是整个图的边,就行了。

3.1.3 Problem 2: Implement Graph

以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。

3.1.3.1 Implement ConcreteEdgesGraph

 这个地方是通过边构图,所以这里我采取的是,实现一个Edge的类,然后ConcreteEdgesGraph类中,首先用Set 的模式存储了在图中点,通过一个List来存储Edge,来实现存储多条边。

 其中,Edge,中存贮边的两个端点,边的权重,等信息,为了保护其安全性,这里全部采取了private final 。

 其他的add(),remove(),set(),vertice(),source(),target(),等函数,都依照graph的要求完成了。
 add()只要在vertices里面直接添加点就行了,判断是否已经存在该点,存在返回false,不存在添加返回true

 remove(),首先在edges里面判断有没有与该点相关的边,全部删去,然后再在vertices里面去除该点,如果在vertices中已经存在该点返回true,不存在该点返回false;

 set(),如果新添加的边的权重为0,则通过遍历,比较边的端点,找到该边,直接删除,并返回其原来边权值。如果不为0,若图中没有该边,直接添加,如果有,就找到该边后,更改其值,并返回,原来值。

 vertice()直接返回类中的vitices set类型就好了

 source(),target()两个,都是遍历所有的边,找到边两个点,如果和他们相同,就把这条边的信息,添加到一个返回最终结果的,map里面去,最后返回就行了。

 CheckRep 的思路是,图中有向边的数量一定小于等于(n-1)*n,n为点的数量。

 ToString的思路是,通过对edges的遍历,返回所有边的toString,就返回了图中所有的边。

 下面的是edges的测试思路和代码

 测试结果截图和Coverage截图

3.1.3.2 Implement ConcreteVerticesGraph

 这个地方采用邻接表的方式来存储图,所以相当于,vertices里面存储的是初始点,的邻接表。有他所到的每一个目标节点,及他们的边的权值
 对于ConcreteVerticesGraph,他的所有fields都是private final 的,所以保证整个是不可变的

 对于类vertex,他是一个邻接表的形式,有初始点sou,然后就是邻接表Map形式,key是终止点,key对应的为边的权值。

 add()只要在vertices里面直接添加点就行了,建立关于这个点的邻接表,判断是否已经存在该点,存在返回false,不存在添加返回true

 remove(),首先遍历所有的vertices,判断该点是否作为起始点,如果有,删除该点,同时判断,该点是否作为终点,如果有,调用vertex。set()删除该点,如果寻找到该点,返回true,否则返回false

 set(),如果新添加的边的权重为0,则通过遍历,比较起始点,找到该边,直接删除其终点和权值,并返回其原来边权值。如果不为0,若图中没有该边,直接添加,如果有,就找到该边后,更改其值,并返回,原来值。

 vertice(), 把所有作为起始点和终止点的点添加到一个返回的set里面,返回这个set就行了。

 source() 遍历所有的初始点,然后在每个出初始点中寻找,是否有同样该点作为终点,如果有,把该初始点和边的权值,加入一个map中,最后返回map

 target()找到该初始点,该点的邻接表就是答案,但是出于不可变类型的考虑,只需要new一个新的Map,该点所有的终点和边的权值重新添加过去就行了。

 CheckRep()这里的主要思想是,1、先判断vertices中是否含有相同的点,2、vertex不为空,且数量大于等于0

 ToString的思路是,通过对vertices的遍历,返回所有初始点邻接表的toString,就返回了该点图。

 下面是vertex的测试思路和方法


 测试结果和coverage

3.1.4 Problem 3: Implement generic Graph

3.1.4.1 Make the implementations generic

 把所有的String类型改成L类型,泛型就好了

3.1.4.2 Implement Graph.empty()

 我们调用Graph.empty()生成ConcreteEdgesGraph或ConcreteVerticesGraph两种类,无论生成的是何种图,我们得到的结果应该是一致的。
 设计L为Integer的功能测试,对两种不同的Graph.empty()进行测试,测试结果应一致且通过。
 测试和coverage如下:
Verticesgraph

Edgesgraph

3.1.5 Problem 4: Poetic walks

3.1.5.1 Test GraphPoet

 抽象数据类型的策略

 对于题目给定的插入单词,设计好input,通过测试得到预定的output就行了
我的corpus:
if i were a boy i will love you.
输入的Input
i a boy will love you.
得到output应该是
i were a boy i will love you.

 测试思路

 测试结果及其coverage:

3.1.5.2 Implement GraphPoet

 设计poem(),这里我是直接把所有的单词,都当作一个初始点,加入到图里面去,然后,后面一个单词,当作终止点,也加入到图里面,对于input,每个单词y进行遍历,如果该单词y,的后面一个单词x,再图中有target(y)==source(x),说明两个单词中间有相同的单词o,就把这个相同的部分,插入到中间,然后,不断的重复,这个过程,直到,input全部遍历完,得到的就是output了。

这个部分的代码,就解释了上述思路,本来是y->x的,再图中发现了o,于是就改变成了y->o->x了
 设计toString,按照图的结构,把所有单词组成的边输出出来。

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仓库。
在这里给出你的项目的目录结构树状示意图。

P2

3.2 Re-implement the Social Network in Lab1

这里想让我们通过使用P1中的Graph 来实现lab1的社交网络,因为P1已经做得很好了,就只要把L写好,就ok了。

3.2.1 FriendshipGraph类

 FriendshipGraph通过继承ConcreteEdgesGraph类实现图,顶点为Person类型;
 Social Network中的对象存于图的vertices集合中;
 getVertex()在vertices邻接表中找到该点,如果有,返回该person,如果没有,返回null

 addVertex():先调用getVertex(),检查vertices中是否含有待加入结点的名字,若已存在,则输出错误提醒并结束程序,若不存在,将结点加入vertices中,使用Graph的add()方法实现;

 addEdge():检查edges集合中是否存在待加入的边,使用Graph中的set()方法实现;

 getDistance():首先判断起点结点与目标结点是否相同,相同则返回0,不同则执行下一步。

创建队列, 初始化,把所有节点的norque设置为false

先把,其中一点x入队,设置norque值为true,然后开始bfs的顺序,依此搜索与队首元素x相连的节点y,并且他不在队中,norque为false,使其入队,norque值变成true。通过target(x)方法实现找到与队首x相连的点,

其中每个入队的点的距离等于,队首点距离+1;最后找到终点为止。就是上图的w[]
存储点的距离。
其中p用来判断是否存在路径,如果不存在,则为false输出-1;
 将双向图变为单向图后,测试用例结果符合预期。

3.2.2 Person类

 设置对象属性有:name、norque(是否在队列中,在getDistance()用到);
 设计构造函数Person(string name)如下

3.2.3 客户端main()

 这里采用模拟人物关系的方式,借用Lab1的数据,直接添加人物,和他们的关系,然后输出他们的距离就行了。
 所以直接先new一个FriendshipGraph
 然后new了很多个Persoin
 然后添加Person
 然后添加Person之间的关系
 然后调用getdistance得到他们的距离,然后输出就好了

3.2.4 测试用例

 这里分别测试addVertex(),addEdge(),getDistance()就行了,测试策略如下

 addVertex()的测试方法,添加person然后,用一个set的集合同时添加相同的person,比较graph.vertice(),和集合set就行了

 getDistance()的测试方法,直接调用自己的测试数据与自己计算的值作比较就好了。

 addEdge()的测试方法,因为上述的getDistance()已经正确了,这里可以调用getDistance()判断是否有边。当然,最正确的方法,还是,通过P1测试set的一样,通过target或source来判断。

3.2.5 提交至Git仓库

如何通过Git提交当前版本到GitHub上你的Lab3仓库。
在这里给出你的项目的目录结构树状示意图。

P3

3.3 Playing Chess

3.3.1 ADT设计/实现方案

设计了哪些ADT(接口、类),各自的rep和实现,各自的mutability/ immutability说明、AF、RI、safety from rep exposure。
必要时请使用UML class diagram(请自学)描述你设计的各ADT间的关系。
 类型与接口有:

 Piece类的实现:
Piece就代表了一个棋子,它包含一些棋子的信息,放置在position上

 Position类,
 这里的position上面放置棋子,piece,然后,位置是board上的属性

 Board类
 Board是棋盘,上面有num*num个位置position,然后棋盘是放置在game游戏上的

 其中我把Actions设置成了接口类型,由Game继承这个接口
这样就能把动作的指令放在Action里面了,原本是放在了Game里面的

 Player类
Player代表玩家,玩家是Game中的一部分,严格被Game控制,所以保证了安全性。

 Game类的构造,

我通过构造函数,来决定这是一盘什么游戏,如果go就走go,是chess就走chess,其实本次实现因为与规则无关,所以不同的棋类,除了棋盘大小,没有本质的区别,所以我这里,在构造函数那里,改变棋盘大小就可以了。
围棋,直接画出18*18的棋盘就行了,象棋除了画棋盘,还需自己提前落子。就解决了。

 Game中的主要的方法的实现:
其实就是继承了接口Action的一些方法的具体实现

Put的实现:
经过一大堆的错误判断,排除操作问题的错误后,只要直接,在该棋子处,判定其in状态为真,棋盘位置(x,y)放置该棋子就行了。
Move的实现:
出去和上述类似的错误判断外,他的主要功能就是

把,(lx,ly)处的棋子移动到(rx,ry)处,因为出于不可变性的问题,我们这里移动棋子new了一个新的但信息相同的棋子,保障了安全性

Remove的实现
经过错误判断后,只要把该位置的棋子的in类型改为false就行了,直接判定该棋子不再棋盘上。

Eat实现:

经过复杂的判断,这里的eat操作,其实,只要移除对方位置(rx,ry)的棋子吗,然后把自己的棋子移动过去就行了。

Sum实现:

直接暴力遍历board的每个position,然后判断他们的whos,来判断棋子的归属和数量就行了。

Out的实现:
这里的out方法,是用来输出整个棋盘用的,方便操作

Check方法:
判断棋子的归属,只要判断piece的whos就好了。然后输出

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

 MyChessAndGoGame为用户控制端,控制操作人员输入的命令。
 先通过读入字符串的形式,读入一些game信息,玩家姓名等信息,

 再给出操作指令

让用户,根据指令操作
 然后通过读入一行一行字符串的形式,读入每条指令,并用string.split分开,然后一次对于每个string转化成其他类型的信息,进行方法的调用
这里用到了switch,对于每个情况进入每个不同的方法

再每次循环后面,也就是指令后面都有一个game.out的方法,输出棋盘,
然后再end结束之后,会把走棋历史输出出来。

 运行截图

这里是一个简单的额移动棋子的操作

统计了一下各棋子数量,
最后输出走棋历史

3.3.3 ADT和主程序的测试方案

介绍针对各ADT的各方法的测试方案和testing strategy。
介绍你如何对该应用进行测试用例的设计,以及具体的测试过程。
每个测试的测试策略如下:

设计的具体情况如下:
Testput():

这里remove和put类似,move和eat类似,这里就不重复截图了。

对于sum方法,注意在方法调用前,调用后,反复测试。

下面是测试结果的截图:

后话

ADT好难学啊
真的感觉就是噩梦

> 对于所有的图片,这里都不给出,主要是防止抄袭,
> 所以,这个文章大概记录一下我的的痛苦吧

你可能感兴趣的:(一袋米要扛二楼)