2022年春季学期
计算学部《软件构造》课程

Lab 2实验报告

姓名

薄文

学号

120L020513

班号

2003004

电子邮件

604094538@qq.com

手机号码

18104568559

 

目录

1 实验目标概述···· 1

2 实验环境配置···· 1

3 实验过程···· 1

3.1 Poetic Walks· 1

3.1.1 Get the code and prepare Git repository· 1

3.1.2 Problem 1: Test Graph <String>· 1

3.1.3 Problem 2: Implement Graph <String>· 1

3.1.3.1 Implement ConcreteEdgesGraph· 2

3.1.3.2 Implement ConcreteVerticesGraph· 2

3.1.4 Problem 3: Implement generic Graph<L>· 2

3.1.4.1 Make the implementations generic· 2

3.1.4.2 Implement Graph.empty()· 2

3.1.5 Problem 4: Poetic walks· 2

3.1.5.1 Test GraphPoet· 2

3.1.5.2 Implement GraphPoet· 2

3.1.5.3 Graph poetry slam·· 2

3.1.6 Before you’re done· 2

3.2 Re-implement the Social Network in Lab1· 2

3.2.1 FriendshipGraph类···· 2

3.2.2 Person类···· 3

3.2.3 客户端main()· 3

3.2.4 测试用例···· 3

3.2.5 提交至Git仓库···· 3

4 实验进度记录···· 3

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

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

6.1 实验过程中收获的经验和教训···· 4

6.2 针对以下方面的感受···· 4

1.  实验目标概述

本次实验训练抽象数据类型(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并据此设计测试用例。

2. 实验环境配置

在IDEA下使用测试用例代码覆盖度插件

https://github.com/ComputerScienceHIT/HIT-Lab2-120L020513

3. 实验过程

3.1 Poetic Walks

本次实验给出了一个图接口,要求我们建立一个边图类、一个点图类分别继承自这个图接口,并且在里面实现一系列方法,并且实现抽象数据型,并用这个图的抽象数据型完成poet的工作。主要目的是练习ADT的规约设计和ADT的实现。

3.1.1 Get the code and prepare Git repository

按照实验中给的地址从github上下载,然后再使用IDEA的插件,将连接到本次实验的仓库

3.1.2 Problem 1: Test Graph <String>

思路:这是针对Graph<String>设计相应的测试策略,主要针对里面的每个方法进行等价类划分的测试。

过程:如下图所示,为所有方法的测试策略。

覆盖率测试如下:

3.1.3  Problem 2: Implement Graph <String>

3.1.3.1 Implement ConcreteEdgesGraph

3.1.3.1.1 实现EDGE类:

(1).EDGE中的字段应该包括边的长度,边的起点和终点,所以定义私有类型的这些变量如下图:

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

Edge

初始化构造方法,初始化新边的两个点和边的权值

checkRep

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

getSource

返回边的一个点source

getTarget

返回边的另外点target

getWeight

返回边的weight

toString

返回一条边的字符串,形式为“起点->终点权重为xx”

(3).AF,RI和Safety from rep exposure如下图:

3.1.3.1.2 实现ConcreteEdgesGraph类:

(1).ConcreteEdgesGraph的字段中应该包括顶点set表和边list表,定义私有类型的表如下图所示:

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

ConcreteEdgesGraph

构造方法

checkRep

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

add

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

set

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

remove

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

vertices

返回所有的点集

sources

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

targets

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

toString

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

(3).AF,RI和Safety from rep exposure如下图:

(4).测试策略:

继承Graph的测试策略,只需要增加toString的测试即可:

测试结果:

检查覆盖率:

3.1.3.2 Implement ConcreteVerticesGraph

   3.1.3.2.1 实现Vertices类:

(1).Vertices的字段中应当包括点的名字,点的源点表Map,点的终点表Map,定义私有类型的表如下图所示:

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

Vertex

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

checkRep

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

getPoint

返回点的名字point

getSource

返回以该点为起点的所有点和边构成的Map

getTarget

返回以该点为终点的所有点和边构成的Map

AddSource

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

AddTarget

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

removeSource

在源点表中删除某起始点,并返回旧的边长

removeTarget

在终点表中删除某终点,并返回旧的边长

toString

得到一个点的字符串表示

(3).AF,RI和Safety from rep exposure如下图:

3.1.3.2.2 实现ConcreteVerticesGraph类:

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

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

ConcreteVerticesGraph

构造方法

checkRep

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

add

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

set

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

remove

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

vertices

返回所有的点集

sources

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

targets

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

toString

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

(3).AF,RI和Safety from rep exposure如下图:

(4).测试策略:

继承Graph的测试策略,只需要增加toString的测试即可:

测试结果:

检查覆盖率:

3.1.4 Problem 3: Implement generic Graph<L>

3.1.4.1 Make the implementations generic

使用泛型实现即可:

在修改过程中通过IDEA提示的错误修改即可完成:

​​​​​​​

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​​​​​​​

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

(2). AF,RI和Safety from rep exposure如下图:

(3).在GraphPoet中需要实现的方法如下图所示:

GraphPoet

输入文件的路径,一行一行读入,储存在List中,然后每次取相邻的元素,在图中添加新的边

checkRep

检查不变性,必须保存从语料库文件生成的图

poem

输入需要进行扩充的字符串,声明声明一个StringBuilder保存,每次读取一个词,当前词作为source,下一个词作为target,然后在garph中寻找source的终点表中是否有与target的源点表中相同的元素,并且找到权值最大的和的点加入source和target之间,具体实现为:

返回扩充后的字符串

toString

调用ConcreteEdgesGraph中的toString方法,将整个图中所有点的指向转化为一条字符串输出

​​​​​​​3.1.5.3 Graph poetry slam

在提供代码的基础上增加一个toString的输出,运行main函数,输出如下:

 ​​​​​​​3.1.6 Before you’re done

通过IDEA的git插件,可以上传到github

项目名称: Lab2-120L020513

                     src

                        P1

                           graph

                                ConcreteEdgesGraph.java

                                ConcreteVerticesGraph.java

                                Graph.java

                           poet

                                GraphPoet.java

                                Main.java

                                mugar-omni-theater.txt

                       test

                         P1

                            graph

                                 ConcreteEdgesGraphTest.java

                                 ConcreteVerticesGraphTest.java

                                 GraphInstanceTest.java

                                 GraphStaticTest.java

                            poet

                                 GraphPoetTest.java

                                 empty.txt

                                 manyline.txt

                                 noempty.txt

                                 oneline.txt

                                 weight.txt

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

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

​​​​​​​3.2.1 FriendshipGraph

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

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

FriendshipGraph

构造方法

addVertex

在图中增加新Person,只需要调用ConcreteEdgesGraph中的add即可:

addEdge

为某个人增加朋友,p1为这个人,p2为增加的朋友,直接调用ConcreteEdgesGraph中的set即可:

getDistance

得到两个人之间的最短距离。与Lab1类似,稍加修改即可:

实现此功能我们需要了解先广方式求最短路径的方法。首先我们需要知道若两个Person对象为同一个,则返回0,若有人不存在社交网络汇总,返回-2。然后定义一个Map集合bfs和一个Person队列Sta,队列Sta用来储存广搜的遍历结果,bfs的Map集合用来储存广搜的所有元素及他们与第一个元素的距离。具体实现方法为首先将第一个元素p1入队,并且把第一个元素p1和下标0入集合,当队列非空时,弹出队首元素top,并且得到top在集合Sta中的下标p,然后定义一个Map为friends,执行SocialNet.targets(p)得到队首元素的所有朋友的Map,然后用for循环,对刚才的friend执行keySet()得到p3,只要p3中的元素与p2不同,就把这些元素全部入队,并且把这些元素及下标distance+1放入集合。只要队列非空,继续执行以上步骤,直到找到某个元素与p2相同并且返回这个元素在集合bfs中的下标。如果直到队列为空还没找到p2,则返回-1:

​​​​​​​3.2.2 Person类

(1)Person定义私有类型的表如下图所示:

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

Person

构造方法

GetName

返回本人名字,直接返回即可

getFlag

返回广度优先的标识

setFlag

设置标识位

(3).AF,RI和Safety from rep exposure如下图:

​​​​​​​3.2.3 客户端main()

根据题意与Lab1的要求相同

 

运行后如下

​​​​​​​3.2.4 测试用例

测试策略:

测试结果:

检查覆盖率:

​​​​​​​ ​​​​​​​

3.2.5 提交至Git仓库

通过IDEA的git插件,可以上传到github

项目名称: Lab2-120L020513

                      src

                         P2

                           FriendshipGraph.java                                

                           Person.java

                      test

                         P2

                           FriendshipGraphTest.java

  1. 实验进度记录

日期

时间段

计划任务

实际完成情况

5.6

18:00-22:00

P1中边图类

未完成

5.7

16:00-22:30

完成P1边图类

完成

5.8

19:00-23:00

P1点图类

未完成

5.9

18:00-22:30

完成P1点图类

完成

5.10

18:00-22:00

完成P1中的诗集类

未完成

5.11

17:50-22:20

P1诗集类

完成

5.12

19:00-22:30

P1中点图和边图的test

完成

5.13

20:00-22:40

完成P1除了测试外的报告

完成

5.14

14:00-20:30

P1的所有测试

完成

5.15

13:00-21:00

完成P2及测试

完成

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

遇到的难点

解决途径

刚开始写P1时还没有讲解AF,RI,Safety from rep

exposure,不知道有什么用

后来上课明白了进行了补充

不理解泛型的意思

查找资料和后来上课明白了

不是太理解继承的关系

csdn求助,后来课堂上也进行了讲解

  1. 实验过程中收获的经验、教训、感想
    1. 实验过程中收获的经验和教训

在自行设计多种类来实现功能的情况下,自己设计的很多类之间有很多重复和矛盾的部分,很多关系弄不清楚,代码很冗杂,测试起来也较麻烦。对于java还是不够熟练,需要多加练习.

    1. 针对以下方面的感受
  1. 面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?

答:面向对象能够每次完成对某个对象的编程,与面向过程的编程的思路完全不一样。

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

答:泛型能够适应更多的变化,更加灵活。

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

答:能够保证代码的正确性,及时修改。不适应。

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

答:可以提高代码的利用率,减少重复。

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

答:防止内部变量被外部修改。很繁琐,写但了更好。

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

答:工作量很大,难度不太大,主要写注释占用太多时间,写测试也用很多时间,报告的内容太多,需要截图太多。deadline很合适。

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

希望有更多的中文注释,英文看着很难受。

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐