哈工大软件构造 Lab2 设计实现

2021年春季学期

计算学部《软件构造》课程

**Lab 2

目录

1 实验目标概述 3

2 实验环境配置 3

3 实验过程 5

3.1 Poetic Walks 5

3.1.1 Get the code and prepare Git repository 5

3.1.2 Problem 1: Test Graph <String> 5

3.1.3 Problem 2: Implement Graph <String> 6

3.1.4 Problem 3: Implement generic Graph<L> 12

3.1.5 Problem 4: Poetic walks 14

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

3.1.7 Before you’re done 16

3.2 Re-implement the Social Network in Lab1 18

3.2.1 FriendshipGraph类 18

3.2.2 Person类 18

3.2.3 客户端main() 19

3.2.4 测试用例 19

3.2.5 提交至Git仓库 20

4 实验进度记录 21

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

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

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

6.2 针对以下方面的感受 22

实验目标概述

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

实验环境配置

IDEA自带代码覆盖率工具,只需右键单击测试文件,选择Run with Coverage即可:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里给出你的GitHub Lab2仓库的URL地址(Lab2-学号):

https://github.com/ComputerScienceHIT/HIT-Lab2-1190200828

实验过程

Poetic Walks

此实验的目的是练习设计、测试和执行ADT。该阶段给出了一个图的接口,要求建立一个边图类一个点图类来分别继承自该图接口,在里面实现一系列方法并实现抽象数据型以完成poet。

Get the code and prepare Git repository

从GitHub获取该任务的代码:

git clone https://github.com/rainywang/Spring2021_HITCS_SC_Lab2/

在本地创建git仓库:

在自己创建的本地仓库中,打开git bash输入git init然后与git远端仓库进行关联即可。

Problem 1: Test Graph <String>

思路:

这是针对Graph<String>设计相应的测试策略,即对Graph<String>的每个方法进行测试。

过程:

下面的截图为接口Graph中的部分被方法测试策略,其中vertices()方法在add、set等方法的测试中已经测试过,因此不再单独测试:
在这里插入图片描述

在这里插入图片描述

其他测试策略不再一一展示。

Problem 2: Implement Graph <String>

Implement ConcreteEdgesGraph
  1. Edge类的实现

  2. 初始化构造方法:

    定义边起点source和边终点target,以及边权值weight。

  3. AF、RI、Safety:
    在这里插入图片描述

  4. Edge中需要实现的方法:

  5. Edge:初始化构造方法,即初始化边的两个顶点和边权值;

  6. checkRep:检查表示不变性;

  7. getSource:返回边起点;

  8. getTarget:返回边终点;

  9. getWeight:返回边权值;

  10. toString:字符串形式描述一条边,形式为”Weight of the edge from source to
    target is weight.\n”

  11. Edge类中各方法的测试策略:
    在这里插入图片描述
    在这里插入图片描述

其他测试策略不再一一展示。

  1. 测试结果:
    在这里插入图片描述

  2. ConcreteEdgesGraph类的实现

  3. 初始化构造方法:

    定义顶点集vertices和边表edges。

  4. AF、RI、Safety:
    在这里插入图片描述

  5. ConcreteEdgesGraph中需要实现的方法(结合已设计的Graph测试用例):

  6. ConcreteEdgesGraph:构造方法,不需要初始化变量;

  7. checkRep:检查表示不变性;

  8. add:添加一个顶点到顶点集vertices,不能重复加入;

  9. set:输入起点source、终点target和边权值weight,若起点或终点不在vertices内,则调用add将其加入。若weight
    < 0,则返回-1;当weight = 0时,若边存在,则删去该边;当weight >
    0时,若该边不存在,则将该边直接加入edges,否则应删去原边后再将新的边和权值加入edges。改变了原边权值时,返回原边权值,边不存在则返回0;

  10. remote:去除某一点以及与它有关的所有边;

  11. vertices:返回顶点集vertices;

  12. sources:输入终点,返回所有指向该点的起点及边的权值;

  13. targets:输入起点,返回该点指向的所有终点及边的权值;

  14. toString:字符串形式输出图,每一条边的输出格式都与Edge类中toString方法中的返回形式相同。

  15. 测试结果(包含Graph测试用例测试结果和toString的测试结果):
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

Implement ConcreteVerticesGraph
  1. Vertex类的实现

  2. 初始化构造方法:

    定义图中点的名字vertex,Map
    sources用于存储指向点vertex的所有起点以及它们之间的边的权值,Map
    targets用于存储点vertex指向的所有终点以及它们之间边的权值。

  3. AF、RI、Safety:
    在这里插入图片描述

  4. Vertex类中需要实现的方法:

  5. Vertex:初始化构造方法;

  6. checkRep:检查表示不变性;

  7. getVertex:返回点的名字;

  8. addSource:向sources中添加指向该点的起点及它们间边的权值。若weight <
    0,则返回-1;当weight = 0时,若该起点存在,则在sources中删去该起点;当weight
    >
    0时,直接sources.put进行添加或者替换。改变了原边权值时,返回原边权值,边不存在则返回0;

  9. addTarget:向targets中添加该点指向的终点及它们间边的权值。操作与addSource相类似;

  10. getSources:返回sources;

  11. getTargets:返回targets;

  12. removeSource:从sources中删去一个指向该点的起点;

  13. removeTarget:从targets中删去一个该点指向的终点;

  14. toString:返回该点以及sources、targets。

  15. 测试结果:
    在这里插入图片描述
    在这里插入图片描述

  16. ConcreteVerticesGraph类的实现

  17. 初始化构造方法:

    定义顶点列表。

  18. AF、RI、Safety:
    在这里插入图片描述

  19. ConcreteVerticesGraph类中需要实现的方法:

  20. ConcreteVerticesGraph:构造方法,不需要初始化变量;

  21. checkRep:检查表示不变性;

  22. add:初始化一个新的顶点并将其添加到到顶点列表vertices,若该点已经存在于图中,不能重复加入;

  23. set:输入起点source、终点target和边权值weight,若起点或终点不在vertices()内,则调用add方法将其加入。关于weight不同取值的操作于Vertice类的addSource、addTarget方法中完成;

  24. remote:去除某一点以及与它有关的所有边;

  25. vertices:返回顶点集vertices;

  26. sources:输入终点,返回所有指向该点的起点及边的权值;

  27. targets:输入起点,返回该点指向的所有终点及边的权值;

  28. toString:字符串形式输出图,每一个顶点的sources和targets的格式都与Vertex类中toString方法中的返回形式相同。

  29. 测试结果:
    在这里插入图片描述
    在这里插入图片描述

Problem 3: Implement generic Graph<L>

Make the implementations generic

将之前实现的类和方法均使用泛型实现即可,即将各类中一开始用String型定义的变量,如起点source、终点target等相关的String类型变量均换为L。

接下来,按照IDEA的报错,将代码的其他位置的String替换为泛型L即可。

Implement Graph.empty()

返回一个Graph的具体实现即可。

补充其他类型的标签:
在这里插入图片描述

测试结果:
在这里插入图片描述

在这里插入图片描述

Problem 4: Poetic walks

Test GraphPoet

GraphPoet类中GraphPoet与poem方法的测试策略及测试用例:
在这里插入图片描述

【注】因为GraphPoet方法没有任何输出,图变量graph为private,所以无法直接测试该方法,所以直接测试两个方法;

1_empty.txt:空白文件,图为空, b必定不存在,所以返回值与input一致;

2_easy.txt:简单测试用文件,要插入的b均唯一

3_difficult.txt:比较复杂的测试用文件,要插入的b不唯一

GraphPoet类中toString方法的测试策略:
在这里插入图片描述

【注】考虑到该方法的测试,将Graph的具体实现更改为边图类,因为边图类的toString输出比点图类输出简单,更易书写测试用例。

Implement GraphPoet
  1. 初始化构造方法:

    定义字符串构成的图graph。

  2. AF、RI、Safety:
    在这里插入图片描述

  3. 需要实现的方法:

  4. GraphPoet:先将文件corpus中的所有点读入,考虑到其中存在相同的点,集合Set并不适用,因此将所有点都读入列表。循环遍历列表,每次读入一前一后两个点作为起点和终点,建成一条边,其权值为该边在corpus中出现的次数;

  5. poem:生成poem,设计如下:
    在这里插入图片描述

  6. toString:调用Graph接口中的toString方法,将整个图中所有点的指向转化为一条字符串输出。因为Graph接口的具体实现在设计测试文件时改为了边图类,所以输出格式即为边图类的toString方法的输出。

Graph poetry slam

在main函数中加入一个输出调用toString方法,运行结果如下:
在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Before you’re done

如何通过Git提交当前版本到GitHub上你的Lab2仓库。

git init

git add .

git commit -m “First commit:Poetic Walks has been done”

git remote add origin
https://github.com/ComputerScienceHIT/HIT-Lab2-1190200828

git push origin master -u -m

在这里给出你的项目的目录结构树状示意图:
在这里插入图片描述

Re-implement the Social Network in Lab1

用在Poetic
Walks已经实现的Graph<L>来实现Lab1中的FriendshipGraph类的各个方法,使之能通过测试文件,并且运行main函数的结果与Lab1一致。

FriendshipGraph类

  1. 初始化构造方法:

    生成空图graph。

  2. AF、RI、Safety:
    在这里插入图片描述

  3. FriendshipGraph类中要实现的方法:

  4. main:与Lab1一致;

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

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

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

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

Person类

  1. 初始化构造方法:

    定义人的名字name

  2. AF、RI、Safety:
    在这里插入图片描述

  3. Person类中要实现的方法:

    Person:初始化构造方法,即初始化name;

客户端main()

将Lab1中的main直接复制过来即可,运行结果如下:
在这里插入图片描述

该结果与Lab1中main的运行结果一致。

测试用例

测试用例与Lab1一致,新增方法getGraph在其他测试中已测试完毕,不再进行单独测试;

测试结果(main无法参与测试,所以代码覆盖度较低):
在这里插入图片描述
在这里插入图片描述

提交至Git仓库

提交过程同Poetic Walks;

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EHvUECld-1625645261706)(media/21c74acf10e1062c719496a9d029fc77.png)]

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

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

  1. 经过学习,对于Java的一些概念更加了解;

  2. 对于Java中自带的一些方法还是很不熟悉,很多方法都要靠百度现场搜,以后会多加积累,多加练习;

  3. 总是因为一些小错误而debug很长时间,以后要避免循环溢出等小错误;

  4. 因为IDEA太聪明了,导致手写代码基本不可能,以后要多加记忆代码,加强手写代码能力。

针对以下方面的感受

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

    ADT的实现过程比较困难,但是一旦实现了ADT,后续工作就会变得十分简单;

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

    本次实验来讲并没有什么差异,因为开始时未用泛型时,也没有用到与特定数据类型相关的方法;

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

    优势:有目的的编程,有助于保证代码的正确性;

    暂时不适应;

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

    提高代码效率,降低实现难度;

  5. P3要求你从0开始设计ADT并使用它们完成一个具体应用,你是否已适应从具体应用场景到ADT的“抽象映射”?相比起P1给出了ADT非常明确的rep和方法、ADT之间的逻辑关系,P3要求你自主设计这些内容,你的感受如何?

    首先,P3不存在;其次,肯定不适应,因为P1都只是勉强能写;

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

    意义:防止内部变量被外部修改,提高代码安全性;

    我会尽量坚持,虽然繁琐,但可以提高代码安全性;

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

    工作量比较大,主要体现在写代码、代码注释、测试策略以及实验报告的撰写;难度对我来说比较高,因为对Java还是不算熟悉,很多方法需要现查;ddl比较合理,毕竟三周时间,但很容易造成所有工作堆在最后完成,建议学习计算机系统实验,将整个实验分为几个具有联系的小型实验发布,每周一交;

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

    Java是一个很聪明的语言,而IDEA也是一个很聪明的编程工具;

    实验中给出代码注释基本上都是英文,本次实验中,我的函数注释基本上也是用的英文,但是测试策略、代码注释还是中文,以后会慢慢改进。
    设计ADT并使用它们完成一个具体应用,你是否已适应从具体应用场景到ADT的“抽象映射”?相比起P1给出了ADT非常明确的rep和方法、ADT之间的逻辑关系,P3要求你自主设计这些内容,你的感受如何?

    首先,P3不存在;其次,肯定不适应,因为P1都只是勉强能写;

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

    意义:防止内部变量被外部修改,提高代码安全性;

    我会尽量坚持,虽然繁琐,但可以提高代码安全性;

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

    工作量比较大,主要体现在写代码、代码注释、测试策略以及实验报告的撰写;难度对我来说比较高,因为对Java还是不算熟悉,很多方法需要现查;ddl比较合理,毕竟三周时间,但很容易造成所有工作堆在最后完成,建议学习计算机系统实验,将整个实验分为几个具有联系的小型实验发布,每周一交;

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

    Java是一个很聪明的语言,而IDEA也是一个很聪明的编程工具;

    实验中给出代码注释基本上都是英文,本次实验中,我的函数注释基本上也是用的英文,但是测试策略、代码注释还是中文,以后会慢慢改进。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值