提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
这次实验内容相较上一次难度提升了一个层次,虽然给的时间多但是三周内有三门考试,安排还是很紧凑,下面将讲一下我对Lab2和ADT的理解,以及实验中不多的注意点,一方面自己总结记录,另一方面方便学弟学妹们少走弯路。
一、插件工具的引入
一个是EclEmma,另一个是SpotBug(非必须,主要是用于对代码进行静态检测)。两个工具的导入都很简单,连接如下:
eclipse覆盖率插件——EclEmma的安装和使用
使用SpotBug进行静态代码检查
二、ADT的实现
首先,预习的时候最好先把实验手册和pdf先通读一遍,把实验各个类的spec,包括测试类都通读一遍,这样加深了对实验内容的理解,上手也比较方便。这次实验一方面是实验一的深入,也为下一章内容做铺垫。随着课程的深入,会对ADT的理解越来越深刻,然后对代码进行修改,再深入再理解的迭代过程,测试也是同理。
这部分编写的时侯,按照实验步骤即可,并无大的问题,先编写完备的测试代码,然后实现Edge和Vertex,再实现Graph的两个实现方法,这样的步骤能省去许多麻烦。在这个过程中,反复推敲的应该是对Edges或Vertex可变类型或不可变类型的各种操作是否安全,注意及时防御式拷贝或是返回不可修改的视图:示例如下:
//private final修饰属性
private final Set<L> vertices = new HashSet<>();
//防御式拷贝
Edge<L> newEdge=new Edge<L>(source,target,weight); edges.add(newEdge);
//不可修改的视图
return Collections.unmodifiableMap(sourceMap);
三、ADT的应用
1.Poetic Walks
此部分实现明显可分为两个步骤:
第一步是实现图的输入,要注意语料库的内容可能有多行,一行,或空。且上下行元素是相连的,另一个应该注意的地方是若两个元素之间有多个空格,应该也能返回正确结果(个人认为这里有点较真了,但实现也无困难,加之罢了)。关于文件读写有了实验一的经验后也没有困难。需要注意的就是先判断单词是否已经存在,边是否已经存在还有大小写的转换。
关于读入的示例代码如下:
try {
while((line=br.readLine())!=null)
{
int columns_number;
String[] Stringword =line.split(" +");
columns_number=Stringword.length;
ArrayList<String> EveryRow=new ArrayList<String>();
for(int j=0;j<columns_number;j++)
{
EveryRow.add(Stringword[j]);
}
wordcorpus.add(EveryRow);
}
}catch (Exception e) {
e.printStackTrace();
System.out.println("file open error");
}finally {
br.close();
}
第二步是实现边的寻找,有了Graph这个ADT之后,实现十分容易。当然这个类的Checkrap,RI,AF,spec也需要填写。
2.FriendshipGraph
完成了上述内容后,此步也无困难,注意点为Person类需要重写equal和Hashcode。而重写equal时侯有许多注意事项,可以按照模板编写,示意如下:
注意Override和Overload的区别,参数必须为Object obj。同时需要先进行对象类型,空的比较判断。这里除了getClass,也可以使用Instanceof,两者区别如下:
java中getClass()方法简介
getClass与instanceof 的区别
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person otherPerson=(Person)obj;
if(otherPerson.PersonName().equals(this.name))
{
return true;
}
return false;
}
四、三次改进
1.可变类型与不可变类型
不可变类型需要我们重写equal与Hashcode。而可变数据类型由于更多基于行为等价性,故我们并不需要重写equal和Hashcode,PPT的例子十分生动阐述了原因。在Vertex类中,如果实现了equal的功能,看似是方便了判断,但由于很多地方使用到了Vertex的集合类,这将导致contain等方法调用时候发生错误!!!
此外,关于重载与重构必须弄清楚,equal中传入的参数必须为Obj obj,而不能是具体的类,这样就变成了overload。
2.测试的改进
在第三周的实验课上,老师修正了我们对测试的误解。正如习题课上所说,对可变数据类型的测试,不仅需要测试变化是否完成,还需要观测变化对其他属性是否有影响。就本问题而言,体现在Set方法的时侯,不仅要判断是否返回正确值,还应该判断是否其他边没有发生变化。获取所有的边可以通过二维Map容器实现,框架示例如下:
private Map<String,Map<String, Integer>> MapreturnEdge(Graph<String> testgraph)
{
Map<String,Map<String, Integer>> a=new HashMap<String,Map<String, Integer>>();
for(String testString:testgraph.vertices())
{
Map<String, Integer> testMap=new HashMap<String, Integer>();
testMap=Collections.unmodifiableMap(testgraph.targets(testString));
a.put(testString, testMap);
}
//结合此函数,在set之后两次获得二维Map判断是否相等。
return Collections.unmodifiableMap(a);
}
3.可复用性的初探
在第三周上课的时候,开始进入最后一部分的学习,引入了LSP原则,值得注意的是,若接口定义抛出异常,而在接口的实现中抛出异常的话相当于是削弱了后置条件,这样并不符合LSP原则。在实验二中,我们的测试策略及实现方法进行了许多的空判断和抛出异常,都是不合格的,需要重新修改。
总结
其他可能对实验有用的链接如下:(实际上便是写文章的时侯忘了当时收藏的原因了哈哈哈)
JUnit assertEquals 两个对象或集合类型
The method assertEquals(Object, Object) is ambiguous for the type Assert解决办法
Java异常被抛出或被捕获之后,代码是否继续执行的问题
这就是实验二的全部总结教训了(写于计算机系统考试周,较为匆忙)。