2022年春季学期
计算学部《软件构造》课程
Lab 2实验报告
姓名 | |
学号 | |
班号 | |
电子邮件 | |
手机号码 |
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
1.实验目标概述
根据实验手册简要撰写。
本次实验训练抽象数据类型(ADT)的设计、规约、测试,并使用面向对象
编程(OOP)技术实现 ADT。具体来说:
l 针对给定的应用问题,从问题描述中识别所需的 ADT;
l 设计 ADT 规约(pre-condition、post-condition)并评估规约的质量;
l 根据 ADT 的规约设计测试用例;
l ADT 的泛型化;
l 根据规约设计 ADT 的多种不同的实现;针对每种实现,设计其表示
(representation)、表示不变性(rep invariant)、抽象过程(abstraction
function)
l 使用 OOP 实现 ADT,并判定表示不变性是否违反、各实现是否存在表
示泄露(rep exposure);
l 测试 ADT 的实现并评估测试的覆盖度;
l 使用 ADT 及其实现,为应用问题开发程序;
l 在测试代码中,能够写出 testing strategy 并据此设计测试用例。
2.实验环境配置
在官网下载了java的jdk,IDEA以及Git。并在IDEA中安装配置了JUnit。
实验需要在 Eclipse IDE 中安装配置 EclEmma(一个用于统计 JUnit 测试用例的代码覆盖度的 plugin)。在IDEA中自带测试代码覆盖度的功能。可以在test类点击Run with Coverage,就能实现这个功能。
给出我的的GitHub Lab2仓库的URL地址(Lab2-学号)。
https://github.com/ComputerScienceHIT/HIT-Lab2-120L020212
3.实验过程
请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1Poetic Walks
在这里简要概述你对该任务的理解。
目的是练习设计、测试和实现ADT。
我们将要实现的类型是Graph<L>,这是一种抽象数据类型,用于带有标记顶点的可变加权有向图。
其中有:
- 可变图:顶点和边可以添加到图中,也可以从图中删除
- 有向边:边从源顶点到目标顶点
- 加权边:边与正整数权重相关联
- 标记的顶点:顶点由某些不可变类型的标签区分。
在最后实现GraphPoet,一个使用单词关联图生成诗歌的类。
3.1.1 Get the code and prepare Git repository
如何从GitHub获取该任务的代码、在本地创建git仓库、使用git管理本地开发。
使用git clone指令获取代码。
建立一个目录,通过git init命令就可以把这个目录变成git管理的仓库。
通过各种git命令就可以管理本地开发了。
3.1.2 Problem 1: Test Graph <String>
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
首先修改Graph.empty()和vertices方法。
测试运行成功。
GraphInstanceTest测试用例:
除其本身自带的testAssertionsEnabled(测试assert是否可用)
testInitialVerticesEmpty(测试初始时,点集是否为空)。
- testAdd:测试加入点的功能。
- testSet:测试添加、修改删除边权的功能。
- testRemove:测试删除点集合中的点的功能。
- testVertices:测试返回点集合的功能。
- testSources:测试指向某点的点集合以及边权的功能。
- testTargets:测试某点指向的点集合以及边权的功能。
具体实现看代码。
3.1.3 Problem 2: Implement Graph <String>
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。3.1.3.1 Implement ConcreteEdgesGraph
Edge类:
private int weight;//边的权重
private L source;//起点
private L target;//终点
public Edge(L source, L target, int weight):增加一个edge
方法:
public void setweight(int weight):改变weight
public L source():返回source
public L target():返回target
public int weight():返回weight
//toString()
public String toString():返回字符串表示的edge,自动生成的就行
ConcreteEdgesGraph类:
protected final Set<L> vertices = new HashSet<>();//存储所有顶点
private final List<Edge<L>> edges = new ArrayList<>();//存储所有边
方法:
public boolean add(L vertex):增加一个顶点
public int findedge(L source, L target):找到边在list中的位置,返回一个index
public int set(L source, L target, int weight):增加一条有向边
public boolean remove(L vertex):移除一个顶点
public Set<L> vertices():返回包含所有顶点的集合
public Map<L, Integer> sources(L target):返回某个顶点为终点的所有边
public Map<L, Integer> targets(L source):返回某个顶点为起点的所有边
3.1.3.2 Implement ConcreteVerticesGraph
Vertex类:
private L name;//顶点的名字
private Map<L, Integer> allsource = new HashMap<>();//返回该顶点为起点的所有边
private Map<L, Integer> alltarget = new HashMap<>();//返回该顶点为终点的所有边
public Vertex(L name):增加一个vertex
方法:
public L name():返回顶点名字
public Map<L, Integer> getsource():返回allsource
public Map<L, Integer> gettarget():返回alltarget
public void setsource(L source, int weight):在allsource中增加一个映射
public void settarget(L target, int weight):在alltarget中增加一个映射
//toString()
public String toString():自动生成
ConcreteVerticesGraph类:
private final List<Vertex<L>> vertices = new ArrayList<>();//储存所有顶点
方法:
public L name():返回顶点名字
public Map<L, Integer> getsource():返回allsource
public Map<L, Integer> gettarget():返回alltarget
public void setsource(L source, int weight):在allsource中增加一个映射
public void settarget(L target, int weight):在alltarget中增加一个映射
3.1.4 Problem 3: Implement generic Graph<L>
3.1.4.1 Make the implementations generic
在工具栏->编辑->查找/替换…中将ConcreteEdgesGraph类和ConcreteVerticesGraph类的所有String替换为L,注意toString方法的返回值依旧为String;但是测试用例中依旧是String不变。
3.1.4.2 Implement Graph.empty()
在Graph中找到empty方法,它需要一个实例类,我选择的是 ConcreteEdgesGraph类(Edges和Vertexs选哪个无所谓),对里面的类型进行参数化后(在类型后面加上),还需要再方法声明的static后面也加上(因为不能对非静态类型 L 进行静态引用)
3.1.5 Problem 4: Poetic walks
3.1.5.1 Test GraphPoet
3.1.5.2 Implement GraphPoet
private final Graph<String> graph = Graph.empty();
方法:
public GraphPoet(File corpus) throws IOException():从corpus中读取原文,处理后存在graph中。
public String poem(String input): public String poem(String input):基于graph将input改为诗歌并返回。
3.1.5.3 Graph poetry slam
由于目录要求,对路径进行更改。
3.1.6 使用Eclemma检查测试的代码覆盖度
3.1.7 Before you’re done
请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
git add .
git commit
git push
在这里给出你的项目的目录结构树状示意图。
3.2 Re-implement the Social Network in Lab1
在这里简要概述你对该任务的理解。
利用上节已经实现的ConcreteEdgesGraph<L>或ConcreteVerticesGraph<L> 替换为 Person。并在FriendshipGraph 中提供 addVertex()、addEdge()和 getDistance()三 个方法 :
针对 addVertex()和 addEdge() ,你需要尽可能复用ConcreteEdgesGraph或 ConcreteVerticesGraph中已经实现的add()和set()方法。针对getDistance()方法,基于所选定的 ConcreteEdgesGraph 或ConcreteVerticesGraph的 rep 来实现,而不能修改其 rep。
要求不变动 Lab1 的 3.3 节给出的客户端代码(例如 main()中的代码),即同样的客户端代码仍可运行。重新执行Lab1里所写的JUnit测试用例,并测试在本实验里新实现的 FriendshipGraph 类仍然要表现正常。
3.2.1 FriendshipGraph类
给出你的设计和实现思路/过程/结果。
private final Graph<Person> graph = Graph.empty();
方法:
public boolean addVertex(Person person):增加一个人到关系网中
public boolean addEdge(Person source, Person target, int weight):在关系网中增加一个关系
public int getDistance(Person a, Person b):获取关系网中两个人的距离,使用BFS
3.2.2 Person类
给出你的设计和实现思路/过程/结果。
private String name;//人的名字
public int distance;//方便BFS而设置的距离
public Person(String name):增加一个人
方法:
public String name():返回name
public int distance():返回distance
3.2.3 客户端main()
给出你的设计和实现思路/过程/结果。
使用原Lab1的main函数。
3.2.4 测试用例
给出你的设计和实现思路/过程/结果。
也是使用原Lab1的测试用例。
测试结果
覆盖率
3.2.5 提交至Git仓库
如何通过Git提交当前版本到GitHub上你的Lab3仓库。
git add .
git commit
git push
在这里给出你的项目的目录结构树状示意图。
4. 实验进度记录
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
日期 | 时间段 | 计划任务 | 实际完成情况 |
2022/5/27 | 14:00-17:00 | 完成P1 | 未完成,GraphPoet尚未完成 |
2022/5/28 | 19:00-20:00 | 完成GraphPoet | 完成 |
2022/5/29 | 18:30-20-15 | 完成P2 | 完成 |
5. 实验过程中遇到的困难与解决途径
遇到的难点 | 解决途径 |
对泛型类不太熟悉 | 上网查阅相关知识 |
英文翻译吃力 | 使用百度翻译帮助理解 |
P2中的getDistance函数出错,返回的全是-1 | 发现是没有对遍历的代码进行修改,需要调用vertices()方法 |
6.实验过程中收获的经验、教训、感想
Java的功能强大,但各种类型细分太多,有些使用不来,其中类似泛型等的理解也需要下一定的苦功夫。
6.2 针对以下方面的感受
(1)面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?
在面向 ADT 编程的时候更多的需要考虑代码的可复用性,需要使整个的编程更加具有普适性;而面向应用场景编程,更加直接和方便,但是适用的范围明显更小。
(2)使用泛型和不使用泛型的编程,对你来说有何差异?
使用泛型能够将自己的ADT适用于更加多的情况中,比如图的顶点的名字可以是字符串、可以是整型、也可以是浮点数,扩大了应用的范围,但是泛型化过程中会有一定的难度,比如有时候会出现令人困惑的黄色下划线警告。
(3)在给出ADT的规约后就开始编写测试用例,优势是什么?你是否能够适应这种测试方式?
优势是更好查出错误,有可能受到思维的局限,设计出的测试用例找不出错 误,并且在每次写下代码或是修改代码后,都能快速的进行回归测试,节约 了重复写测试的时间。
能逐渐适应。
(4)P1设计的ADT在多个应用场景下使用,这种复用带来什么好处?
可以将已有的代码进行复用来尽量节省时间。
(5)P3要求你从0开始设计ADT并使用它们完成一个具体应用,你是否已适应从具体应用场景到ADT的“抽象映射”?相比起P1给出了ADT非常明确的rep和方法、ADT之间的逻辑关系,P3要求你自主设计这些内容,你的感受如何?
无P3。
(6)为ADT撰写specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后编程中坚持这么做?
提升可维护性,可读性,安全性。因为不能确保ADT一写完以后就不会添加新的功能,提前发现错误,避免数据外泄,愿意。
(7)关于本实验的工作量、难度、deadline。
工作量比较大,难度高。时间紧凑。且与考试时间冲突。
(8)《软件构造》课程进展到目前,你对该课程有何体会和建议?
难度很高,希望在学这门课前学校能先开一门Java语言课。