软件构造Lab2问题解决思路及感想

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 1
3.1.3 Problem 2: Implement Graph 1
3.1.3.1 Implement ConcreteEdgesGraph 2
3.1.3.2 Implement ConcreteVerticesGraph 2
3.1.4 Problem 3: Implement generic Graph 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 使用Eclemma检查测试的代码覆盖度 2
3.1.7 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

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

二:实验环境配置
配置EclEmma:
访问http://www.eclemma.org安装配置EclEmma,
参考以下学习EclEmma使用:
https://blog.csdn.net/zczzyezgycsz8888/article/details/84914143
在这里给出你的GitHub Lab2仓库的URL地址(Lab2-学号):
https://github.com/ComputerScienceHIT/HIT-Lab2-1190200405

三:实验过程
请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1Poetic Walks
任务:在实现一个图模块的基础上进行语句的扩展,美其名曰诗词漫步。
(1)完善Graph接口类,拓展String为泛型L。
(2)实现ConcreteEdgesGraph类中的add、set、remove、vertices、source、target,复写toString。(边的思想)
(3)实现实现ConcreteVerticesGraph类中的add、set、remove、vertices、source、target,复写toString。(点的思想)
(4)利用实现的Graph类,实现GraphPoet类,完成诗词漫步。
3.1.1Get the code and prepare Git repository
在Get the code步骤中,无法连接MIT的Athena服务器,从以下地址获 取 初始代码:
https://github.com/rainywang/Spring2020_HITCS_SC_Lab2/tree/master/P1
3.1.2Problem 1: Test Graph
add方法的划分:
vertex:已存入graph的点、未存入graph的点。
set方法划分:
source ,target:已存入graph的点、未存入graph的点。
weight:0,非0。
remove方法划分:
vertex:已存入graph的点、未存入graph的点。
vertices:
graph为空、graph不为空。
source方法划分:
target:有起始节点指向此点、无起始节点指向此点。
target方法划分:
source:有指向的点、无指向的点。

3.1.3Problem 2: Implement Graph
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
3.1.3.1Implement ConcreteEdgesGraph
实现Edge:
AF:source、target、weight组成的抽象数据型到有向边集合的映射
RI:weight>0、source和target不为空
Safety from rep exposure:设置信息为private、definsive copies

设计Edge思路;
Fileds: 说明:
Private final L source 起始节点
Private final L target 目标节点
Private final int weight 边权值

设计Edge 过程及思路:
方法: 作用:
public L getsource( ) 返回起始节点
public L gettarget( ) 返回目标节点
public int getweight( ) 返回边权值
public String toString( ) 覆盖Object方法的toString方法,返回节点和权值信息

实现ConcreteEdgeGraph:

AF:vertices、edges到有向图的映射
RI:edges有起始节点,weight>0、vertex必须在vertices集合中
Safety from rep exposure:设置信息为private、definsive copies

设计ConcreteEdgeGraph思路:
方法: 作用:
public boolean add(L vertex) 添加入顶点,返回boolean
public int set(L source,L target,int weight) 输入起始及目标节点、权值,添加有向边,根据约定返回int
public boolean remove (L vertex) 由集合中移除目标节点
public Set vertices( ) 返回所有顶点的集合
public Mapsources(L target) 返回连接某一目标节点的全部起始节点的map集合
public Maptargets(L source) 返回连接某一起始节点的全部目标节点集合
public String toString ( ) 返回说明图连接的字符串

设计ConcreteEdgeGraph过程:
方法: 实现过程:
public boolean add(L vertex) 调用vertices.add,加入顶点
public int set(L source,L target,int weight) weight<0,提示错误,返回-1
weight= =0 遍历集合移除边,返回边的权值
weight>0 遍历集合更新边的权值
public boolean remove (L vertex) 遍历顶点集合,移除此点
public Set vertices( ) 复制一新的vertices集合并返回
public Mapsources(L target) 根据target寻找以其为终点的边,建立一键值对为(起始点,权重)的Map,并返回
public Maptargets(L source) 根据source寻找以其为终点的边,建立一键值对为(点,权重)的Map,并返回。
public String toString( ) 构建字符串,遍历集合中的边,将信息添加至此字符串,最终返回。

设计ConcreteEdgeGraph结果:

3.1.3.2Implement ConcreteVerticesGraph

实现vertex:

AF: source映射为指向这个点的所有的起始点和边、target由这个点指向的所有目标点和边
RI:每个边的权值大于0
Safety from rep exposure:
将点的名字name、source、target设置为private

实现vertex思路:
Fields 作用
private final L name 节点名字
private final Map sources =new HashMap< >( ) 所有以name为目标节点的< 起始节点,边的权重>
private final Map targets =new HashMap< >( ) 所有以name为起始节点的< 目标节点,边的权重>

方法 作用:
public L getname( ) 返回节点名字
public Map getsources( ) 返回节点所有起始节点及边
public Map gettargets( ) 返回节点所有目标节点及边
public boolean removesource(L source) 移除节点的起始节点集合中的某一点
public boolean removetarget(L source) 移除节点的目标节点集合中的某一点
public boolean addsource( Lsource,int weight) 根据边的权值不同,添加、删除节点的起始节点集合内容
public boolean addtarget(L target,int weight) 根据边的权值不同,添加、删除节点的起始节点集合内容
public String toString( ) 覆盖Object方法的toString方法,返回起始节点、目标节点和其边权值信息

实现vertex过程:
方法: 实现过程:
public L getname( ) 返回节点名字
public Map getsources( ) HashMap
public Map gettargets( ) HashMap
public boolean removesource(L source) HashMap.remove,若返回值为null,则返回0,如果返回值不为null,返回source指向此点的边的权值
public boolean removetarget(L source) HashMap.remove,若返回值为null,则返回0,如果返回值不为null,返回此点指向target的边的权值。
public boolean addsource( Lsource,int weight) weight= =0,调用removesource移除此点
weight > 0,调用HashMap.put,无此点,建立。有此点,更新weight并返回原weight。
weight < 0 ,报错,返回-1。
public boolean addtarget(L target,int weight) weight= =0,调用removesource移除此点
weight > 0,调用HashMap.put,无此点,建立。有此点,更新weight并返回原weight。
weight < 0 ,报错,返回-1。
public String toString ( ) 构建字符串,连接起始节点集合与目标节点集合转变的字符串,并返回。

实现ConcreteVerticesGraph:

设计ConcreteVerticesGraph思路:
方法: 作用:
public boolean add(L vertex) 添加入顶点,返回boolean
public int set(L source,L target,int weight) 输入起始及目标节点、权值,添加有向边,根据约定返回int
public boolean remove (L vertex) 由集合中移除目标节点
public Set vertices( ) 返回所有顶点的集合
public Mapsources(L target) 返回连接某一目标节点的全部起始节点的Map集合
public Maptargets(L target) 返回连接某一起始节点的全部目标节点集合
public String toString ( ) 返回说明图连接的字符串

实现ConcreteVerticesGraph过程:
方法: 实现过程:
public boolean add(L vertex) 遍历顶点集合,重复false,否则,加入
public int set(L source,L target,int weight) 遍历顶点集合,匹配source和target,并调用vertex.addtarget,vertex.source将其加入相应的起始点或目标点集合。
public boolean remove (L vertex) 遍历顶点集合,移除此点
遍历顶点结合,匹配顶点的sources和targets集合,移除其中的这个点。
public Set vertices( ) 构建一新的vertices集合并返回
public Mapsources(L target) 遍历顶点集合,匹配到以与target重名的节点,调用vertex.getsource,返回HashMap
public Maptargets(L source) 遍历顶点集合,匹配到以与source重名的节点,调用vertex.getsource,返回HashMap
public String toString( ) 构建字符串,遍历集合中的点,将信息添加至此字符串,最终返回。

3.1.4Problem 3: Implement generic Graph

3.1.4.1Make the implementations generic
实现思路:
代码中类型不确定,使用泛型。

实现方法:
使用占位符L,将具体的类改为:
public class ConcreteEdgesGraph implements Graph { … }
class Edge { … }
public class ConcreteVerticesGraph implements Graph { … }
class Vertex { … }
更改类的方法中的String为占位符L。

3.1.4.2Implement Graph.empty()
设计思路、过程及结果:
将ConcreteEdgesGraph作为Graph默认的实例类,也可用ConcreteVerticesGraph:

使用Integer、Float类进行泛型测试:

3.1.5Problem 4: Poetic walks
3.1.5.1Test GraphPoet
测试策略:
文件规格:空文件、一行输入、多行输入
连接桥的选择:权值为1、权值不等
toString : 空文件、非空文件

3.1.5.2Implement GraphPoet
AF: source:加权有向图寻找两点与第三点间权值最大通路的那个点到在两词间添加中间词的映射
RI:中间词为两点间桥、wetght>0
Safety from rep exposure:
必要时使用definsive copies

实现GraphPoet思路:
方法: 作用
public GraphPoet(File corpus) 打开文件,构建图结构。
public String Poem(String intput) 读取输入,根据图结构添加中间词并输出。
public String toString( ) 输出图结构
实现GraphPoet过程:
方法: 实现过程:
public GraphPoet(File corpus) 打开文件,按行读取文件,按行空格切割行得到单个词存入数组中,每次取数组中相邻两词,根据其在途中新加边或更新边。
public String Poem(String intput) 读取输入,空格分割得到单个词,每次读取一个词,以当前词为source,在图结构中寻找其targets,下一词为target,在图结构中寻找其sources,找出tagets和sources中相同且权值相加最大的元素作为中间词插入,生成字符串并返回。
public String toString( ) 调用Graph实例中的toString方法,返回字符串。

3.1.5.3Graph poetry slam
西格里夫·萨松代表作《于我,过去,现在以及未来 》:

3.1.6使用Eclemma检查测试的代码覆盖度
P1:
测试ConcreteEdgesGraph

测试ConcreteVerticesGraph

测试GraphStaticeTest

测试GraphPoetTest

P2:
测试FriendshipGraphTest

3.1.7Before you’re done

请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
1.首先在本地git init 建立一个仓库。
2.通过git add将文件上传至缓冲区
3.通过git commit上传至本地仓库
4.最终通过git push 上传至远程仓库

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

3.2Re-implement the Social Network in Lab1
继承P1中ConcreteEdgesGraph或者ConcreteVerticesGraph类实现 FriendshipGraph。

3.2.1FriendshipGraph类

设计思路:
使用ConcreteEdgesGraph类,将原代码更改为有向图结构:

设计过程:
方法: 作用及实现过程:
public void addVertex(Person newperson ) 调用graph.add方法,添加顶点,失败,输出提示信息。
public void addEdge(person a,person b) 调用graph.set方法,添加有向边。
public ConcreteEdgesGraphgetGraph( ) 返回Person类构成的图结构。
public int getDisance(Person a, Person b) 返回两人间社交距离,采用广度优先搜索思想,具体实现使用队列,逐步查找,直到找到Person b,并返回距离。
public static void main(String[ ] args) 客户端,实现根据问题要求。

3.2.2Person类
设计思路:
方法: 作用及实现过程:
public Person(String name) 创建Person。
public String getname( ) 返回姓名。

3.2.3客户端main()
根据Lab1题设得到设计思路过程及结果:

3.2.4测试用例

测试addVertex:
测试添加新点是否正确

测试addEdge:
测试添加新边是否正确

测试getDistance:

测试getgraph:
测试能否得到图结构

3.2.5提交至Git仓库
如何通过Git提交当前版本到GitHub上你的Lab3仓库。
1.首先在本地git init 建立一个仓库。
2.通过git add将文件上传至缓冲区
3.通过git commit上传至本地仓库
4.最终通过git push 上传至远程仓库

四:实验过程中遇到的困难与解决途径

遇到的难点 解决途径
英语十分垃圾,读题时虽有翻译但仍不理解其意 请求同学帮助,理清题意。
如何记录一对重复的文本库中的字符串个数 创建一HashMap,以收尾相连字符串为键,每出现一次,其键值增加一
如何检索输入语句文本库中有中间词 返回第一个词的targets,返回第二个词的sources,比较两个集合中共有的键值最大的那个就为中间词(桥)。
五:实验过程中收获的经验、教训、感想
6.1实验过程中收获的经验和教训
学好英语很重要,网页翻译有时并不靠谱,软件构造PPT也大部分为英文,努力学习英语,与国际化接轨。
对于java中各种类中方法并不熟悉,难以熟练应用,在算法上也存在很大不足
6.2针对以下方面的感受
(1)面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?
ADT做到了使用数据的方式与数据类型本身分开,较直接面向应用场景的编程更注重信息隐藏,对于一个特性,尽可能在一个模块中实现操作。
(2)使用泛型和不使用泛型的编程,对你来说有何差异?
使用泛型对后续类的应用更具帮助,但同时也更加抽象,不使用泛型更加具体些。
(3)在给出ADT的规约后就开始编写测试用例,优势是什么?你是否能够适应这种测试方式?
基于测试的编程,起到了推动软件构建的作用,更能减少软件bug。对于此种方式尚处于适应阶段,不过感觉上要好些。
(4)P1设计的ADT在多个应用场景下使用,这种复用带来什么好处?
模块化,每个组件和模块都可以与系统其余部分拆开设计实现,与重用,更加适合于积累代码量,提升了工作效率,对以后的程序构建起到很大作用。
(5)P3要求你从0开始设计ADT并使用它们完成一个具体应用,你是否已适应从具体应用场景到ADT的“抽象映射”?相比起P1给出了ADT非常明确的rep和方法、ADT之间的逻辑关系,P3要求你自主设计这些内容,你的感受如何?
学时压缩,并未构建P3.
(6)为ADT撰写specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后编程中坚持这么做?
是,
能够帮助理清逻辑、检查正确性及起到信息隐藏的作用,提升程序安全性。
愿意这么做。
(7)关于本实验的工作量、难度、deadline。
实验设计的很巧妙,结合java课程来讲会更好些。难度适中,deadline适中
(8)《软件构造》课程进展到目前,你对该课程有何体会和建议?
建议将课程PPT换成中文的吧,非母语教学总归不入母语效率高。
实验指导书也建议汉化一下,某些专有名词翻译过来看不懂,理解题意也很困难。

你可能感兴趣的:(Lab2,java)