本次实验训练抽象数据类型(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并据此设计测试用例。
主要环境配置均与实验一相同,不再过多介绍。
此外,还需安装Elemma插件进行代码覆盖度检测。
直接在Eclipse中的Help-Eclipse Marketplace中查找该插件,安装即可。
GitHub Lab2仓库的URL地址为:
https://github.com/ComputerScienceHIT/HIT-Lab2-1190201027
该实验要求首先自行设计实现一个Graph类的ADT,并利用顶点和边两种方式分别将其实现。设计完成后,利用已经实现的Graph类完成一个基于有向图的GraphPoet算法,使用预先的英语短语生成一个语法图,来在英文句子中的任意两个相邻单词之间尝试插入一个符合语法的额外单词。除了类和算法本身的实现以外,实验还要求考察对抽象过程,表示不变性及其检测,表示泄漏的预防,测试用例编写等方面的知识,多角度的考察了有关ADT编写的相关知识。
使用以下git代码进行远程代码的获取:
git init
git remote add origin @
git add .
git commit -m
git push origin master
-
-
- Problem 1: Test Graph <String>
-
设计思路及过程:
该阶段测试分为多部分内容:
GraphStaticTest,Graph类的静态方法测试。需要在此部分添加对于Graph类静态泛型方法进行测试。使用两种不同的数据类作为顶点进行图的构建,然后用toString方法得出的字符串与预期图所对应的字符串进行比较。
GraphInstanceTest,Graph类的其他方法测试。需要为Graph类的所有其余方法进行测试,优先测试toString这一observer方法,测试完成后依据该方法观察其他方法得出的结果是否符合预期。
各方法使用的测试策略如上。编写代码实现对这些测试方法的覆盖即可。
ConcreteEdgesGraphTest及ConcreteVerticesGraphTest,GraphInstanceTest的测试子类,在通用的InstanceTest基础上补充对于toSting方法和两种方法各自的内部类测试。由于代码已经满足了两个实现方式都能产生相同的toString字符串,该方法的测试已经包含在GraphInstanceTest中,不再重写。对于各自内部类的测试,思路相似,均通过创建对应实例,然后使用observer方法进行成员查看,将其与预期情况进行比较来实现。
设计结果:
4个测试类中的全部测试均正确通过。使用Elemma查看测试类的代码覆盖情况,基本覆盖全部代码。(ConcreteEdgesGraph代码覆盖度略低,主要由于checkRep方法需要Edges类重写equals和hashCode方法,而hashCode方法并未被其余代码使用)
-
-
- Problem 2: Implement Graph <String>
- Implement ConcreteEdgesGraph
- Problem 2: Implement Graph <String>
-
设计思路及过程:
首先需要实现内部类Edge,用该类来表示有向图中的边,再利用Edge类来实现基于边的有向图,即ConcreteEdgesGraph。
Edge为不可变类,包括了成员 source,target和weight,分别用于表示该有向边对应的起始顶点,终止顶点和边权值。同时Edge类还实现了如上图所示的一系列方法,用于在获取成员信息的同时避免表示泄漏。
实现Edge类后,就可以完成基于边的有向图实现。
ConcreteEdgesGraph中包含以上给定的两个成员。可以通过vertices访问图中包含的全部顶点,也可以通过edges访问图中全部的边,进而可以实现整个有向图。所有的方法实现如下:
add()方法不涉及边操作,仅需要将目标顶点检测输入合法性后添加进vertices集合即可。
set()方法,检测输入合法性后,先删去可能存在的原有边,存储原边的权值信息。再将新边添加进edges表中,返回原边的权值(若无,返回0)。
remove()方法,依据给定的顶点,先在edges表中遍历查找所有起始顶点或终止顶点为给定顶点的边,将其全部删除。完成之后,在vertices中移除该顶点,就彻底从图中移除了顶点。
vertices()方法,由于该实现本身即包含顶点集合,可以直接将其防御性拷贝后返回。
target()和source()方法,在edges表中遍历查找所有起始顶点(或终止顶点)为给定顶点的边,依据这些边的信息查找相应的终止顶点(或起始顶点),将这些顶点全部存入新的列表后返回。
tosString()方法,我为Graph类设定的toString()输出格式为:
[起始顶点1 终止顶点1,权值 终止顶点2,权值……][起始顶点2……]……
则需要遍历vertices中的每个顶点,依次将其作为起始顶点在edges中查找属于该顶点的边,输出对应的终止顶点和边权值。
设计结果:见测试阶段。
-
-
-
- Implement ConcreteVerticesGraph
-
-
设计思路及过程:
首先需要实现内部类Vertex,用该类来表示有向图中的顶点,再利用Vertex类来实现基于顶点的有向图,即ConcreteVerticesGraph。
Vertex为可变类,包括了成员 source,和edges,分别用于表示其顶点信息和以该顶点作为起始顶点的有向边集合。同时Vertex类还实现了如上图所示的一系列方法,用于在获取成员信息和修改相关内容。
实现Vertex类后,就可以完成基于顶点的有向图实现。
ConcreteVerticesGraph中仅包含vertices成员。通过vertices列表存储所有的顶点,可以访问顶点及其相应的边,进而访问图的全部信息,实现表示。所有的方法实现如下:
add()方法,检测输入合法性后,直接将有对应顶点信息的顶点加入vertices列表。之后边的相关操作将会直接在这个顶点对应的Vertex实例内进行。
set()方法,检测输入合法性,通过Vertex的hasEdge()方法确认可能已有的边并删除,存储原边的权值信息。再使用addEdge()方法在这个顶点的Vertex实例内为edges列表添加新的边信息,按要求返回。
remove()方法,先遍历全部顶点,若这些顶点包括指向目标顶点的边,将其删除。再从vertices列表中将目标顶点删除即可。
vertices()方法,遍历全部顶点,将每个Vertex实例中存储的顶点信息存入新建的列表中,最后返回。
tosString()方法,由于以上描述的toString()输出格式恰好对应顶点及其相关的边集合,与Vertex实现十分吻合。直接遍历vertices,将每个Vertex实例的顶点信息和边集合信息按照要求格式输出即可。
设计结果:见测试阶段。
设计思路及过程:
为Graph类设置泛型声明,然后将代码中所有与原String成员实现的相关内容均调整为泛型L即可。
设计结果:
见测试阶段。
-
-
-
- Implement Graph.empty()
-
-
设计思路及过程:
为Graph类实现返回一个实例的静态方法。直接返回以上顶点实现或边实现中的任意一种即可。最后采用了顶点实现。
设计结果:
见测试阶段。
-
-
- Problem 4: Poetic walks
- Test GraphPoet
- Problem 4: Poetic walks
-
设计思路及过程:
在GraphPoet类的specification中已经给出了该类的一个测试数据,故直接使用了该数据进行测试。用实现给定的短语组生成一个GraphPoet实例,再使用该实例为目标句子插入单词。将最终的结果与预期比较即可。同时也测试了文件操作过程。
设计结果:
测试正确通过。使用Elemma查看测试的代码覆盖度,得到的结果很好(显然该测试类不会对Main进行测试)。
-
-
-
- Implement GraphPoet
-
-
设计思路及过程:
GraphPoet类的实现主要包括两部分内容:
constructor方法:该方法需要通过给定的文件字符串进行文件操作,读取其中的短语并依据其中的单词顺序建立一个有向语法图。
GraphPoet类中仅包含一个Graph类的成员,所有的构建均在该成员上实现。首先通过文件操作以及split方法将文件中的短语组划分成一个有序的字符串数组。依次遍历数组中的所有字符串,将这些字符串添加进图中作为顶点。同时需要标记遍历的上一个字符串,与当次遍历字符串比较,若相同,则改为将对应边的权值+1。相同的字符串仅会被添加进图中一次。通过这样的方法完成遍历,就能建立一个有向语法图,并通过边权值标记期望的语法关系。
poem方法:实现通过建立的有向语法图为目标语句插入单词。同样先将目标语句拆分成有序字符串数组。遍历数组,对该数组中的任意两个相邻字符串,在图中进行查找。若存在某个顶点,以其中一个字符串为起始顶点有边,以另一个字符串为终止顶点也有边,则将其标记为待插入字符串。对于多个满足这样条件的顶点,选取对应两边权值最大的顶点作为插入内容。经过一轮遍历后,将最终确定的顶点插入进待输出的字符串中(对没有这样顶点的,不作插入)。遍历整个目标语句,完成全部单词插入后输出即可。
设计结果:
测试类关于该实现的测试正确通过。同时,调用该方法的main也正确执行。设计正确实现。
该部分对Main中未进行修改。
请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。
使用以下git代码将本地代码上传:
git add .
git commit -m
git push -u origin master
项目的目录结构树状示意图如下:
实验目标是实现一个关系网的建立,考察变长数组,最短路径查找的相关知识,且该次实现应主要运用前面实现的Graph类。
设计思路及过程:
在本次实现中,仅使用一个Graph实例成员来完成FriendshipGraph的相关内容。
在实现了Graph类后,可以使用该类的方法显著简化实现代码。
addVertex方法,只需要调用Graph类的add方法即可。
addEdge方法,也只需要进行输入合法性检测后使用Graph类的set方法即可。
getDistance方法:复用了之前的代码,将其改为用Graph类实现做了简单的优化。计算最短路径方法。使用广度优先遍历的算法思路实现,使用一位数组存储起始顶点到其余各顶点的距离,初始为-1(到自身初始为0)。使用队列存储待遍历顶点,将起始顶点入队。再依次执行:每次出队一个顶点,检查是否存在其余顶点,该顶点到其可达且之前的顶点均到其不可达。对这样的顶点,更新数组中其距离为该顶点距离加一,并入队。重复该过程直至队空或到目标顶点的距离已经被计算出。返回计算出的该距离即可。
设计结果:
见3.2.4测试结果。
设计思路及过程:
主要代码内容与前一次实验完全一致。在前一次实验代码的基础上重写了equals和hashCode方法,用于比较两个Person实例是否相同。
-
-
- 客户端main()
-
main依据前一次实验的要求,放在了FriendshipGraph.java中,并未单独新建类。其中代码也与实验给出的代码完全一致,不做修改。
测试正确完成。使用Elemma查看测试代码覆盖度,覆盖度较低,主要由于大量main方法的代码没有执行,重写的hashCode方法也未被执行过。其他代码的覆盖度良好。
-
-
- 提交至Git仓库
-
同3.1.6相同,使用以下git代码将本地代码上传:
git add .
git commit -m
git push -u origin master
项目的目录结构树状示意图如下: