(防扒小助手)
本人CSDN博客:
本人博客园博客(同步CSDN):
如果对你有用的话欢迎点赞关注哟!
3.1.1 Get the code and prepare Git repository
3.1.3 Problem 2: Implement Graph
3.1.4 Problem 3: Implement generic Graph
3.1.6 Before you’re done
本次实验训练抽象数据类型(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 并据此设计测试用例。
(1)安装测试用例代码覆盖度插件
经过网上查阅资料了解到,在IDEA中已经集成了代码覆盖度插件JaCoCo,切换方式如下:
点击Edit Configuration,弹出如下窗口:
在Code Coverage一栏点击Modify勾选红框中的选项,则会弹出切换代码覆盖度工具的选项:
通过咨询软件构造课程老师与助教老师,了解到使用IDEA自带的代码覆盖度工具即可,不需要切换到JaCoCo,因此下文代码覆盖度测试均使用IDEA自带的coverage runner进行测试。
2、GitHub Lab2仓库的URL地址(Lab2-学号)
3.1 Poetic Walks
该任务主要是通过实现一个图的模块来练习ADT的规约设计和ADT的不同实现。
(1)完善Graph接口类,并运用泛型的思想,将String拓展为泛型L类;
(2)实现Graph接口类:以边和点两种方式实现接口;
(3)利用实现的Graph类,应用图的思想,实现GraphPoet类。如果输入的文本的两个单词之间存在桥接词,则插入该桥接词;若存在多个单一桥接词,则选取边权重较大者。
从要求文件中对应网址下载得到实验代码,建立好project,进入目录,打开Git bush
依次输入:
git init
git remote add origin git@github.com:ComputerScienceHIT/HIT-Lab2-120L022408.git
git pull origin master
git add .
git commit -m “init”
git push origin master
测试Graph的静态方法。
为了方便测试Graph的多种实现,在 GraphInstanceTest 中测试了实例方法。
编写测试用例主要利用等价类划分的思想进行测试,测试策略如下:
分别编写覆盖以上条件的测试用例。
运行 Graph S tatic T est得到测试结果如下:
3.1.3.1 Implement ConcreteEdgesGraph
(1)Edge类实现
定义两个private String类型的变量source和target存放每个边的起止点
定义一个private int类型的变量weight保存这条边的权重(长度)
private final String source ,target ;
private final int weight ;
关于AF,RI和rep exposure:
① 构造器 constructor
构造方法,使用上述三个数据域声明一个新的边
public Edge (L source_new,L target_new,int weight_new)
{
this .source = source_new;
this .target = target_new;
this .weight = weight_new;
checkRep ();
}
② 检查表示不变量 checkRep
检查表示不变量,其中source和target必须非空,weight必须大于0
public void checkRep ()
{
assert source !=null ;
assert target !=null ;
assert weight >0 ;
}
③ get方法
get_Source:返回source域
get_Target:返回target域
get_Weight:返回weight域
④ toString方法
返回一个字符串表明这条边是从哪个source到哪个target,其weight是多少。
public String toString ()
{
return source .toString () +”->”+target .toString () +”\t 权重为”+weight +’\n ‘;
}
(2)ConcreteEdgesGraph实现
vertices和edges分别记录当前graph所含有的点和边
private final Set
vertices =new HashSet <>();
private final List>edges =new ArrayList <>();
关于AF,RI和rep exposure:
① add方法
public boolean add (String vertex)
如果顶点不为空,添加一个顶点。如果在vertices的Set集合中成功添加了vertex,则返回true。
② Set方法
public int set (String source,String target,int weight)
输入source,target,weight,确定一条有向边。
具体做法:如weight!=0,移去可能已经存在的相同起始点的边,然后加入新的边,如weight=0,寻找可能已经存在的相同起始点的边,删去。
③ remove方法
public boolean remove (String vertex)
从vertices中删去给定的vertex点,遍历edges,寻找该vertex是否为某条边的起点或者终点,删去相应的边。在使用迭代器遍历时要使用iterator.remove方法保证安全性。
④ vertices方法
public Set
vertices ()
返回vertices集合。注意做到safety from rep exposure ,使用Collections.unmodifiableSet()方法。
⑤ sources方法
public Map
sources (String target)
参数:target。根据传入的target参数寻找以targe为终点的边。返回一个键值对为(点,权重)的map。
实现:建立一个map,利用迭代器遍历edges,如果某个edge的edge.get_Target()和传入参数target相等,则将该边的source和weight存入map中。
⑥ targets方法
public Map
targets (String source)
参数:source。根据传入的source参数寻找以source为起点的边。返回一个键值对为(点,权重)的map。
实现:建立一个map,利用迭代器遍历edges,如果某个edge的edge.get_Source()和传入参数source相等,则将该边的target和weight存入map中。
⑦ 检查表示不变量 checkRep
思路:n个点,最多构成n*(n-1)条有向边,因此存在这种不可变的数学关系
⑧ toString方法
对每条边调用toString方法,整合起来。
public String toString ()
{
String s =””;
for (Edgee :edges )
{
s = s +e .toString ();
}
return s;
}
(3)ConcreteEdgesGraphTest测试
JUnit测试结果如下:
测试覆盖率:
3.1.3.2 Implement Concrete Vertices Graph
(1)Vertex类实现
定义两个private String类型的变量source和target存放每个边的起止点
定义一个private int类型的变量weight保存这条边的权重(长度)
private final String name ;
private final Mapsources ;
private final Maptargets ;
关于AF,RI和rep exposure:
① 构造器 constructor
构造方法,传入参数name创建新的点。
public Vertex (String name)
{
this .name = name;
sources =new HashMap <>();
targets =new HashMap <>();
}
② 检查表示不变量 checkRep
检查表示不变性,各边weight的值应该永远大于0。
③ get方法
get_Name:返回source域
get_Sources:返回weight域
get_Target:返回Targets域
④ set方法
set_Target:为当前点新增一个target,
如果weight为0,删去当前点的target,成功返回删去target的weight,不存在返回0;如果weight不为0,为当前点新增一个target,长度为weight,如果该点已存在,返回旧的weight,否则返回0
set_Source:为当前点新增一个source,
如果weight为0,删去当前点的source,成功返回删去source的weight,不存在返回0;如果weight不为0,为当前点新增一个source,长度为weight,如果该点已存在,返回旧的weight,否则返回0
⑤ remove方法
remove_Source:删去当前点的指定source
public int remove_Source (String source)
{
Integer weight =sources .remove (source);
return weight ==null ?0 :weight ;
}
remove_Target:删去当前点的指定target
public int remove_Target (String target)
{
Integer weight =targets .remove (target);
return weight ==null ?0 :weight ;
}
⑥ toString方法
返回一个字符串表明这个顶点的信息
public String toString ()
{
return String .format(“Vertex %s has %d sources and %d targets”,this .get_Name ().toString (),this .get_Sources ().size (),this .get_Targets ().size ());
}
(2)ConcreteVerticesGraph实现
使用如下数据类型保存顶点的数据:
private final List
>vertices =new ArrayList <>();
关于AF,RI和rep exposure:
① 检查表示不变量 checkRep
所有点的标识不能为空
private void checkRep ()
{
assert vertices !=null ;
}
② add方法
public boolean add (String vertex)
参数:vertex,判断vertices中无重复点就加入
③ Set方法
public int set (String source,String target,int weight)
参数:source, target, weight。先将可能不在vertices中的source点和target加入vertices。随后遍历vertices,找到source对它增加一个target,找到target为它增加一个source,并设置距离。
④ remove方法
public boolean remove (String vertex)
参数:vertex。遍历vertices,如果当前点是vertex,删去(使用iterator.remove方法),如果不是,检查它的source和target是否包含vertex,如果有删去。
⑤ vertices方法
遍历vertices,找到每个点对应的string,添加进set即可。使用防御性拷贝:
public Set
vertices ()
{
Setset =new HashSet <>();
for (Vertexv :vertices )
{
set .add (v .get_Name ());
}
return set ;
}
⑥ sources方法
public Map
sources (String target)
参数:target。根据传入的target参数寻找以targe为终点的边。返回一个键值对为(点,权重)的map。
⑦ targets方法
public Map
targets (String source)
参数:source。根据传入的source参数寻找以source为起点的边。返回一个键值对为(点,权重)的map。
实现:建立一个map,利用迭代器遍历edges,如果某个edge的edge.get_Source()和传入参数source相等,则返回source对应的目标点图。
⑧ toString方法
打印当前顶点图的顶点数量:
public String toString ()
{
return String .format(“This graph has %d vertices”,this .vertices .size ());
}
(3)ConcreteEdgesGraphTest测试
JUnit测试结果如下:
测试覆盖率:
3.1.4.1 Make the implementations generic
将具体类的声明更改为:
public class ConcreteEdgesGraph
implements Graph { … }
class Edge
public class ConcreteVerticesGraph
implements Graph { … }
class Vertex
更新两个实现以支持任何类型的顶点标签,使用占位符L代替String。
充分利用IDEA的智能改错功能快速修改成泛型实现。
3.1.4.2 Implement Graph.empty()
选择ConcreteEdgesGraph来实现Graph.empty()
测试全部通过:
3.1.5.1 Test GraphPoet
关于测试策略:
具体测试:
3.1.5.2 Implement GraphPoet
首先声明:
private final Graph
graph =new ConcreteEdgesGraph ();
关于AF,RI和rep exposure:
① 检查表示不变量 checkRep
所有点的标识不能为空
private void checkRep ()
{
assert graph !=null ;
}
② GraphPoet方法
参数:corpus文件路径。打开文件,读取文件输入,识别序列,构建图结构。
③ poem方法
参数:input。
利用相同方法分割输入字符串,声明一个StringBuilder保存返回结果。每次读取一个词,然后以当前词为source,下一个词为target,在graph中寻找符合此条件的边,记录权值,结束后选择权值最大的,利用StringBuilder. Append方法,将节点名字加入字符串。
④ toString方法
调用ConcreteEdgesGraph的toString方法,输出图结构
public String toString ()
{
return graph .toString ();
}
3.1.5.3 Graph poetry slam
语料库为泰戈尔经典名句集锦
输入输出如下:
通过Git提交当前版本到GitHub上你的Lab2仓库。
git add .
git commit -m “P1 Finished”
git push -u origin master
项目的目录结构树状示意图。
这个实验是基于在Poetic Walks中定义的Graph及其两种实现,重新实现Lab1中的 FriendshipGraph类。我们需要尽可能复用ConcreteEdgesGraph或 ConcreteVerticesGraph中已经实现的add()和set()方法,而不是从零开始。另外基于所选定的 ConcreteEdgesGraph 或 ConcreteVerticesGraph的rep来实现,而不能修改父类的rep。
(1)设计思路
继承ConcreteEdgesGraph
public class FriendshipGraph extends ConcreteEdgesGraph
(2)方法实现
构造一个ArrayList类型的变量person_list存储顶点列表
private final ArrayList
person_list =new ArrayList ();
① public boolean addVertex(Person people)
这个函数是为把参数添加到图中,作为图的一个顶点,直接调用父类的this.add()即可。调用过程中检查顶点列表中是否已出现参数对应的顶点,若重复则打印错误信息并返回false,成功添加顶点则返回true
② public boolean addEdge(Person people1, Person people2)
构建图的要素,在图中添加边。先调用 this.vertices().contains()方法来判断所添加边的顶点是否存在,再判断两顶点之间是否已有边连接,若条件满足,则调用this.set()方法设置边,权重初始化为1并返回true,其余情况返回false。
③ public int getDistance(Person People1, Person People2)
获取两个顶点之间距离的函数,题目要求返回最短距离,因此采用广度遍历的方式,此处需要用到Queue的数据结构,并且设置了一个List来存放已经访问过的person。
Person类根据FriendshipGraph类的需求编写的。它用于描述每个成员的性质,主要是实例化姓名的构造方法,getName()方法,判断姓名是否重复的isSameName方法。
public class Person
{
private final String Name ;
public Person (String Name)
{
this .Name = Name;
}
public String getName ()
{
return this .Name ;
}
public boolean isSameName (String Name)
{
return this .Name .equals (Name);
}
}
main函数主体内容即为实验指导书给定的内容:先new一个FriendshipGraph类的对象,然后添加顶点,添加边。
输出错误类型在实现FriendshipGraph类时已输出,故此处不需要再次判断是否出现错误。
① 正常输出的测试结果
② 注释掉rachel -> ross后的测试结果
测试策略:
根据划分的等价类设计测试用例
测试结果与覆盖度报告:
通过Git提交当前版本到GitHub上的Lab2仓库。
git add .
git commit -m “P1 P2 first finished”
git push -u origin master
本项目的目录结构树状示意图:
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
时间段
计划任务
实际完成情况
2022-05-23
8:00-11:30
浏览报告,查看MIT相关内容要求
按计划完成
2022-05-23
15:00-19:00
完成P1的GraphStaticTest类测试
按计划完成
2022-05-24
9:00-11:00
完成P1的ConcreteEdgesGraph类编写
按计划完成
2022-05-24
15:00-20:00
完成P1的ConcreteVerticesGraph类
按计划完成
2022-05-25
8:15-11:00
实现泛型Graph
按计划完成
2022-05-25
13:00-14:00
浏览Poetic Walks的编写要求
按计划完成
2022-05-25
21:00-23:00
尝试实现Poetic Walks
遇到困难,延期完成
2022-05-26
9:00-17:00
完成Poetic Walks的test和Implement
按计划完成
2022-05-27
8:00-18:00
完成P2的Social Network的改写
按计划完成
遇到的难点
解决途径
不了解IDEA如何实现代码覆盖率测试
查阅网上资料后发现相应解决方案因IDE版本迭代已发生改变。自己摸索解决了实现代码覆盖率测试插件切换的问题。
对面向test的编程思想理解不够深入
复习《软件构造》课程的PPT,上网查阅了相关资料并咨询了同学,完成了对测试代码的编写。
对规约的要求不够理解
通过学习模仿,尝试自己编写相应规约并实现之。
6.1 实验过程中收获的经验和教训
经验:
加深了自己对于泛型的理解和认识,提高了代码编写、ADT设计的能力。编写test测试文件时,有些方法的测试也能覆盖到其他的方法,避免重复测试增加工作量。
教训:
在设计多个类并使之互相配合的方面做得不好,编写代码的逻辑性有待提高。
6.2 针对以下方面的感受
(1)面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?
我感觉,对于面向ADT的编程,类是其主要特点,程序执行过程中,先由主函数进入,定义一些类,根据需要,执行类的成员函数,过程的概念被淡化了。而直接面向应用场景编程的抽象程度不高,虽然逻辑清晰但是代码思路混乱,不利于实现。
(2)使用泛型和不使用泛型的编程,对你来说有何差异?
泛型编程可以使代码被很多不同类型的对象所重用,并使代码具有更好的可读性。
(3)在给出ADT的规约后就开始编写测试用例,优势是什么?你是否能够适应这种测试方式?
优势是不考虑代码的内部实现,只需考虑是否完成了规约中指定的功能。作为java语言的初学者来说我很不适应这种测试方式。
(4)P1设计的ADT在多个应用场景下使用,这种复用带来什么好处?
提高了代码的利用率,减轻编程工作量。
(5)为ADT撰写specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后编程中坚持这么做?
使编写的代码更加安全和可读性更强。愿意这么做。
(6)关于本实验的工作量、难度、deadline。
我认为,考虑到实验时间,与其他专业课的复习时间与考试时间有大量冲突,因此显得本实验工作量十分巨大,难度也很高,deadline十分紧张。
(7)《软件构造》课程进展到目前,你对该课程有何体会和建议?
希望减少工作量,增加课时安排,增加动手实验分数,减少笔试考试分数,合理安排课程开展时间,可以在刚开学时开课或者在小学期开课,更有利于学生能力的提升。
Original: https://www.cnblogs.com/kalesky/p/16327501.html
Author: 何以牵尘
Title: 哈工大软件构造Lab2(2022)
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/593547/
转载文章受原作者版权保护。转载请注明原作者出处!