HITlab2实验总结

1.实验目标概述

本次实验训练抽象数据类型(ADT)的设计、规约、测试,并使用面向对象

编程(OOP)技术实现 ADT。具体来说:

针对给定的应用问题,从问题描述中识别所需的 ADT

设计 ADT 规约(pre-conditionpost-condition)并评估规约的质量;

根据 ADT 的规约设计测试用例;

ADT 的泛型化;

根据规约设计 ADT 的多种不同的实现;针对每种实现,设计其表示

representation)、表示不变性(rep invariant)、抽象过程(abstraction

function

使用 OOP 实现 ADT,并判定表示不变性是否违反、各实现是否存在表

示泄露(rep exposure);

测试 ADT 的实现并评估测试的覆盖度;

使用 ADT 及其实现,为应用问题开发程序;

在测试代码中,能够写出 testing strategy 并据此设计测试用例。

2.实验过程

2.1Poetic Walks

该任务主要是通过实现一个图的模块来练习ADT的规约设计和ADT的不同实现。

(1)完善Graph接口类,并运用泛型的思想,将String拓展为泛型L类;

(2)实现Graph接口类:以边和点两种方式实现接口;

(3)利用实现的Graph类,应用图的思想,实现GraphPoet类。如果输入的文本的两个单词之间存在桥接词,则插入该桥接词;若存在多个单一桥接词,则选取边权重较大者

2.1.1Get the code and prepare Git repository

从老师给定的网站中提取出实验相关代码。

2.1.2Problem 1: Test Graph <String>

根据测试的策略进行划分:

@Test public void addTest()
{

测试相关边是否存在,根据边的有无进行测试

@Test public void setTest()
{

测试相关点是否存在,测试点是否存在

@Test public void toStringTest()
{

测试相应tostring内容是否和输入内容相符

@Test
public void GetSourceTest() {

@Test
public void GetTargetTest() {

@Test
public void GetWeightTest() {

测试结果如下:

2.1.3Problem 2: Implement Graph <String>

2.1.3.1Implement ConcreteEdgesGraph

在Edge中需要实现的方法如下:

checkRep:检查表示不变性,边不为空且权值大于等于0

private void checkRep()
{
    assert this.target!=null;
    assert this.source!=null;
    assert this.weight>0;

getSource:返回边的一个点source

public L getSource(){return this.source;}

getTarget:返回边的另一点target
public L getTarget(){return this.target;}

getWeight:返回边的weight
}public int getWeight(){return this.weight;}

toString:返回一条边的字符串

public String toString()
{

    return "source:" + this.source +"target:" + this.target +"weight:" + this.weight +'\n';
}

在ConcreteEdgesGraph中需要实现如下方法:

checkRep

检查表示不变性,edges长度是大于0的实数,有起始的节点

private void checkRep() {

add

顶点不为空时,添加一个顶点进入点表中

@Override public boolean add(L vertex) {

set

输入source,target,weight,分别为边的起点、终点和权值。若权值为负,返回-1。若权值为正且新边已经存在,则除去原边并添加新边。若权值为正且新边不存在,则直接添加新边。若权值为0且新边已经存在,则出去原边。只要改变了原边权值,都返回原边权值,没有权值则返回0

@Override public int set(L source, L target, int weight)

remove

除去某个点及与它相邻的所有边。只需要遍历edges,寻找是否有边的起点或者终点为该点,直接删去即可,使用迭代器实现。

@Override public boolean remove

vertices

返回所有的点集

@Override public Set<L> vertices()

sources

输入终点,返回与它相连的所有边与其构成的Map

@Override public Map<L, Integer> sources

targets

输入起点,返回与它相连的所有边与其构成的Map

@Override public Map<L, Integer> targets

toString

将整个图中所有点的指向转化为一条字符串并将其输出

@Override
public String toString(){

  1. .测试策略:继承Graph的测试策略,在其基础上增加toString的测试
  2. 测试结果:

 

测试策略:增加toString测试即可

​​​​​​​2.1.3.2Implement ConcreteVerticesGraph

首先要实现Vertice类

实现函数如下

Vertex

初始化构造方法,用点的名字创建

public Vertex(L name)

checkRep

检查表示不变性,每个边的权值应该大于0

private void checkRep()

getName

返回点的名字Name

public L getName()

getSource

返回能到达该点的所有点和边构成的Map

public Map<L,Integer>getSources()

getTarget

返回某个点能到达的所有点和边构成的Map

public Map<L,Integer>getTargets()

setSource

在源点Map中加入某源点,若weight不为0,则将其加入source中(若源点已存在,则更新其weight并返回原weight,不存在则直接构建新点并返回0)。若weight为0,则移除源点(不存在返回0,存在返回原weight)

public int setSource(L source,int weight)

setTarget

在终点Map中加入某终点,若weight不为0,则将其加入target中(若终点已存在,则更新其weight并返回原weight,不存在则直接构建新点并返回0)。若weight为0,则移除终点(不存在返回0,存在返回原weight)

public int setTarget(L target,int weight)

toString

得到一个点的字符串表示

public String toString()
{
    return "" + "name:" + this.name + '\n' + "sources:" + this.sourceMap + '\n' + "targets:" + this.targetMap + '\n';
}

测试类如下:

@Test
public void toStringTest() {

@Test
public void GetNameTest()

@Test

public void GetSourceTest()

@Test

public void GetTargetTest()

@Test
public void AddSourceTest()

@Test
public void AddTargetTest()

@Test
public void SetSourceTest()

@Test
public void SetTargetTest()

接下来需实现ConcreteVerticeGraph类

实现该类需要以下方法

checkRep

检查表示不变性,vertices中不能有重复点

private void checkRep()

add

顶点不为空时,添加一个顶点进入点表中

@Override public boolean add(L vertex)

set

输入source,target,weight,分别为边的起点、终点和权值。若权值为负,返回-1。若权值为正且新边已经存在,则除去原边并添加新边。若权值为正且新边不存在,则直接添加新边。若权值为0且新边已经存在,则出去原边。只要改变了原边权值,都返回原边权值,没有权值则返回0

@Override public int set(L source, L target, int weight)

remove

除去某个点及与它相邻的所有边。只需要遍历vertices,寻找是否有与待删除点相同的名字的点直接删去即可,如果名字不相同,则在该点的源点表和终点表中寻找删去即可,使用迭代器实现。

@Override public boolean remove(L vertex)

vertices

返回所有的点集

@Override public Set<L> vertices()

sources

输入一个终点,返回与它相连的所有边和起点构成的Map

@Override public Map<L, Integer> sources(L target)

targets

输入一个起点,返回与它相连的所有边和终点构成为的Map

@Override public Map<L, Integer> targets(L source)

toString

将整个图中所有点的指向转化为一条字符串输出

@Override public String toString()

测试策略:

与edge相同增加tostring测试类

测试结果如下:

2.1.4Problem 3: Implement generic Graph<L> 

​​​​​​​2.1.4.1Make the implementations generic

使用泛型实现,在修改过程中依照IDEA的报错修改即可

​​​​​​​2.1.4.2Implement Graph.empty()

调用ConcreteEdgesGraph具体实现即可

​​​​​​​2.1.5Problem 4: Poetic walks

任务要求我们实现一个类,利用之前实现的图结构,能够将语料库转化为该种图结构,并且在图中搜索,完成对输入的诗句的句子进行扩充,从而输出扩充后的新句子。

​​​​​​​2.1.5.1Test GraphPoet

测试结果:

 

​​​​​​​2.1.5.2Implement GraphPoet

函数如下:

public GraphPoet(File corpus) throws IOException {

private void checkRep()
public String poem(String input) {

public String toString()

  1. GraphPoet:输入文件的路径,一行一行读入,储存在List中,然后每次取相邻的元素,在图中添加新的边
  2. checkRep:检查不变性,必须保存从语料库文件生成的图
  3. Poem:输入需要进行扩充的字符串,声明一个StringBuilder保存,每次读取一个词,当前词作为source,下一个词作为target,然后在garph中寻找source的终点表中是否有与target的源点表中相同的元素,并且找到权值最大的和的点加入source和target之间.返回扩充后的字符串
  4. toString:调用ConcreteEdgesGraph中的toString方法,将整个图中所有点的指向转化为一条字符串输出

5.3​​​​​​​Graph poetry slam

增加tostring()

Tostring输出结果如下:

2.1.6使用Eclemma检查测试的代码覆盖度 

​​​​​​​2.1.7Before you’re done

 

使用IDEA进行上传

在这里给出你的项目的目录结构树状示意图。

​​​​​​​2.2Re-implement the Social Network in Lab1

 这次实验要求我们基于Poetic Walks中定义的Graph<L>及其两种实现(本人使用的是ConcreteVerticesGraph<L>),实现Lab1中Social NetWorek中的各种功能,并且尽可能复用ConcreteVerticesGraph<L>中已经实现的方法,然后运行提供的main()和执行Lab1中的Junit测试用例,使之正常运行。

​​​​​​​2.2.1FriendshipGraph

(1).FriendshipGraph的字段为Person构成的ConcreteEdgesGraph,定义私有类型的表如下图所示:

(2).在FriendshipGraph需要实现的方法如下图所示:

1.main:与Lab1一致;

2.addVertex:向图中添加点,调用Graph类中的add即可;

3.addEdge:向图中添加边,因为是无向图,调用两次Graph类中的set即可;

4.getDistance:得到两点间的距离,利用DFS算法,通过调用Graph类中的sources等方法即可;

5.getGraph:因为构造方法graph设置为为private,为方便测试,故设置该方法以返回graph

结果:

​​​​​​​2.2.2Person

(1).FriendshipGraph的字段为Person构成的ConcreteVerticesGraph,定义私有类型的表如下图所示:

(2)在FriendshipGraph需要实现的方法如下图所示:

Person加入姓名

addFriend添加Friend信息

getName和getFriend输出一个返回值

​​​​​​​2.2.3客户端main()

使用lab1中的客户端

测试结果如下:

 

​​​​​​​2.2.4测试用例

测试结果如下:

2.2.5​​​​​​​提交至Git仓库

使用idea直接PUSH和COMMIT

在这里给出你的项目的目录结构树状示意图。

4.实验进度记录

请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。

日期

时间段

计划任务

实际完成情况

3.20

18:00-19:00

完成边图集

延期一小时后完成

3.24

19:00-22:00

完成P1中点图集相关内容

按时完成

3.27

17:00-20:00

研究诗歌集

完成

3.29

18:00-22:00

编写测试类

诗歌类测试没有完成

4.4

19:00-21:00

编写P2相关代码

按时完成

4.6

18:30-20:00

编写出P2TEST

按时完成

4.9

14:00-17:00

检验程序并写报告

按时完成

 

5.实验过程中遇到的困难与解决途径

遇到的难点

解决途径

Test测试时出现错误

检查相关报错根据错误更改源代码和test测试代码

测试代码覆盖率时出现报错

更改JDK的版本后可正常使用

编写poet测试用例时总是达不到预期标准

查询关于poet的相关方法,了解到该程序工作相应原理,根据原理编写出合适的测试用例

FriendshipGraph引用图的时候出现问题

查询相关代码将people作为source和target后引用成功

 6​​​​​​​实验过程中收获的经验教训、感想

​​​​​​​​​​​​​​        6.1实验过程中收获的经验教训(必答)

收获:学习到了更多关于JAVA编程语言的相关的知识,可以更好的使用JAV来解决部分问题,可以更熟练的使用测试,以及提交代码。对仓库更加熟悉。

教训:测试类十分重要对于监测代码有着重要作用,在编写测试类时要更加用心

​​​​​​​​​​​​​​       6.2针对以下方面的感受(必答)

                  1.面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?

面向ADT编程要求对编写的程序整体有充分的认识,要考虑接口、抽象类、 实例类的整体设计,要考虑到哪些部分能够复用、哪些部分需要具体实现; 而面向应用场景编程则是就事论事,编程思路简单,但是工作量大,复用性 低。

                 2.使用泛型和不使用泛型的编程,对你来说有何差异?

使用泛型可以使编写好的程序适用于不同的数据类型,如果需要只要在相应 位置添加相应的类型即可,而不需要重复开发,很方便,也能提高软件开发 的效率。不使用泛型,在功能相同而类型不同的情况下,将会导致重复开发, 函数版本冗杂。

                3.在给出ADT的规约后就开始编写测试用例,优势是什么?你是否能够适应这种测试方式?

优势是更加专注于ADT的规范细节和边界情况,从而确保我们的实现是正 确的、完整的、可靠的。这样做可以减少频繁试错时间使得完成时间大大缩 短。

不是十分适应,经常不太习惯。

               4.P1设计的ADT在多个应用场景下使用,这种复用带来什么好处?

提高代码利用率,节约更多时间,减少消耗。

               5.为ADT撰写specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后编程中坚持这么做?

这些工作为了使得编程工作更加准确无误,能够使得本身程序更加完整,操 作减少不必要的麻烦,节约了许多实验时间,使得本身更快完成。

尽可能在以后实验中按照上述要求操作,使得实验更加规范。

               6.关于本实验的工作量、难度、deadline。

工作量较大,难度适中,deadline足够,可以在规定时间完成。

                7.《软件构造》课程进展到目前,你对该课程有何收获和建议?

获得了许多关于Java的编程知识,并且能够更好地对所写代码进行测试,能够更好的实现相应的程序要求,使得代码更完整。

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值