软件构造lab2

  1. 实验目标概述

验训练抽象数据类型(ADT)的设计、规约、测试,并使用面向对象编程(OOP)技术实现 ADT

  1. 实验环境配置

环境:IntelliJ IDEA,jdk21,Junit4

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

https://github.com/ComputerScienceHIT/HIT-Lab2-2022112818.git

  1. 实验过程

请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。

    1. Poetic Walks

完成Graph接口的两实现个类ConcreteEdgesGraph,ConcreteVerticesGraph ,实现Graph数据类型,同时可实现泛型化Graph<L>。其中需要在边与定点中实现add(添加新节点),set(添加新边),remove(移除节点),vertices(获得所有的节点集合),sources获得以target为目标节点的边的起始节点,targets获得以source为起始节点的边的目标节点。

      1. Get the code and prepare Git repository

打开实验要求的github仓库,获取URL后通过git clone克隆到本地的git仓库中。

      1. Problem 1: Test Graph <String>

以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。

Add:添加重复与不重复的点

Set:添加新边,修改边值,用Add验证边的两点是否添加成功

Remove:删除存在或不存在的点,利用Set验证与点相连的边是否存在

Vertices:验证输出点集是否相同

Sources/Targets:测试起始点/终点是否与图中一致

      1. Problem 2: Implement Graph <String>

以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。

        1. Implement ConcreteEdgesGraph

Edge中

字段

private final L source;:边的起点,类型为泛型L,并且使用final修饰

private final L target;:边的终点,类型也为泛型L,同样使用final修饰。

private final int weight;:边的权重,类型为整型,并使用final修饰。

构造函数

Edge(L source, L target, int weight):这是Edge类的构造函数,它接受三个参数来初始化source、target和weight字段。

Getter 方法

public L getSource():返回边的起点。

public L getTarget():返回边的终点。

public int getWeight():返回边的权重。

private void checkRep():这个方法是一个辅助方法,用于检查类的表示不变量是否满足。

public String toString():这个方法重写了Object类的toString()方法,返回边的字符串表示形式。

ConcreteEdgesGraph中

  • add方法

加入一个新的顶点。

直接调用add函数。

② Set方法

输入source,target,weight,建立一条有向边。

具体做法:如weight>0,移去可能已经存在的相同起始点和终点的边,然后加入新的边,如weight=0,寻找可能已经存在的相同起始点的边,删去,如weight<0,提示输入非法。

③ remove方法

从vertices中删去给定的vertex点,遍历edges,寻找该vertex是否为某条边的起点或者终点,删去相应的边。在使用迭代器遍历时使用iterator.remove方法保证安全性。

④ vertices方法

返回vertices集合。

⑤ sources方法

根据传入的target参数寻找以target为终点的边。返回一个键值对为(点,权重)的map。

实现:建立一个map,利用迭代器遍历edges,如果某个edge的target和传入参数target相等,则将该边的source和weight存入map中。

target与source类似,这里不再赘述。

测试结果

        1. Implement ConcreteVerticesGraph

Vertex中

字段

private L name;:顶点的名字,类型为泛型L。

private Map<L, Integer> sources = new HashMap<>();:指向该顶点的边的映射,键为边的起点,值为边的权重。

private Set<L> targets = new HashSet<>();:该顶点发出的边所指向的顶点集合。

构造函数

public Vertex(L name):构造函数,接受一个类型为L的参数作为顶点的名字,并初始化sources和targets字段。

方法

public int set(L source, int weight):设置从source指向当前顶点的边的权重。如果之前存在这样的边,则返回之前的权重;否则返回0。如果source为null或weight不大于0,则不执行任何操作。

public L getName():返回顶点的名字。

public Map<L, Integer> getSources():返回指向当前顶点的边的映射。

public void setSources(Map<L, Integer> sources):设置指向当前顶点的边的映射。public Set<L> getTargets():返回当前顶点发出的边所指向的顶点集合。

public void addTargets(L target):向当前顶点发出的边所指向的顶点集合中添加一个顶点。

public void removeTarget(L target):从当前顶点发出的边所指向的顶点集合中移除一个顶点。

public String toString():这个方法重写了Object类的toString()方法,返回边的字符串表示形式。

ConcreteVerticesGraph中

  • add方法

加入一个新的顶点。

检查点集中没有相同点,若有返回false,若无将点加入点集后返回true。

② Set方法

输入source,target,weight,建立一条有向边。

具体做法:如weight>0,移去可能已经存在的相同起始点和终点的边,然后加入新的边,如weight=0,寻找可能已经存在的相同起始点的边,删去,如weight<0,提示输入非法。

③ remove方法

从vertices中删去给定的vertex点,遍历sources 和 targets,寻找该vertex是否为某条边的起点或者终点,删去相应的边。

④ vertices方法

返回vertices集合。

⑤ sources方法

根据传入的target参数寻找以target为终点的边。返回一个键值对为(点,权重)的map。

实现:建立一个map,利用迭代器遍历edges,如果某个edge的target和传入参数target相等,则将该边的source和weight存入map中。

target与source类似,这里不再赘述。

测试结果

      1. Problem 3: Implement generic Graph<L>
        1. Make the implementations generic

将具体类的声明更改为:

public class ConcreteEdgesGraph<L> implements Graph<L> { ... }

class Edge<L> { ... }

public class ConcreteVerticesGraph<L> implements Graph<L> { ... }

class Vertex<L> { ... }

更新两个实现以支持任何类型的顶点标签,使用占位符L代替String。

        1. Implement Graph.empty()

选择ConcreteEdgesGraph,新建一个空Graph

      1. Problem 4: Poetic walks
        1. Test GraphPoet

分别测试空文档,单个单词与多个单词

        1. Implement GraphPoet

GraphPoet读取文件并根据文件生成图。

利用BufferedReader.readLine方法读取全部输入后用string.split以空格划分,保存在数组中,随后每次取相邻元素,在图中新增边。

poem根据输入字符串和之前构造的图(graph)来生成一首诗

利用相同方法分割输入字符串,声明一个StringBuilder保存返回结果。每次读取一个词,然后以当前词为source,下一个词为target,在graph中寻找符合此条件的边,记录权值,结束后选择权值最大的,利用append方法,将节点名字加入字符串。

toString输出图结构

调用ConcreteEdgesGraph的toString方法,输出图结构

        1. Graph poetry slam
      1. 使用Eclemma/Code Coverage for Java检查测试的代码覆盖度

      1. Before you’re done

请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。

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

git add .

git commit -m "P1 Finished"

git push -u origin master

    1. Re-implement the Social Network in Lab1

基于在Poetic Walks中定义的Graph及其两种实现,重新实现Lab1中的 FriendshipGraph类。

      1. FriendshipGraph

继承ConcreteEdgesGraph<Person>,构造一个ArrayList类型的变量personList存储顶点列表。

addVertex把参数添加到图中,作为图的一个顶点,直接调用父类的this.add()即可。

addEdge构建图的要素,在图中添加边。先调用 this.vertices().contains()方法来判断所添加边的顶点是否存在,再判断两顶点之间是否已有边连接,若条件满足,则调用this.set()方法设置边,权重初始化为1并返回true,其余情况返回false。

 getDistance获取两个顶点之间距离的函数,采用广度遍历的方式。

      1. Person

Person类根据FriendshipGraph类的需求编写的。它用于描述每个成员的性质,主要是实例化姓名的构造方法。

      1. 客户端main()

直接复制实验要求的代码。

注释掉rachel -> ross后的测试结果

      1. 测试用例

与lab1测试思路相同。

      1. 提交至Git仓库

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

git add .

git commit -m "P1 Finished"

git push -u origin master

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

  1. 实验进度记录

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

每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。

不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。

日期

时间段

计划任务

实际完成情况

4.13

15:00-21:00

完成P1的GraphStaticTest类测试

按计划完成

4.14

全天

完成P1的ConcreteEdgesGraph类编写

按计划完成

4.15

15:00-21:00

完成P1的ConcreteVerticesGraph类

按计划完成

4.16

15:00-21:00

实现泛型Graph<L>的转换

按计划完成

4.17

15:00-21:00

实现Poetic Walks

未完成

4.20

全天

完成Poetic Walks

按计划完成

4.21

全天

完成P2的Social Network的改写

完成

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

遇到的难点

解决途径

对面向test的编程思想理解不够深入

查阅相关资料,阅读其它相关代码

对规约的要求不够理解

通过学习模仿,尝试自己编写相应规约并实现之。

目录结构调整有误

目前未解决

  1. 实验过程中收获的经验教训、感想
    1. 实验过程中收获的经验教训(必答)

经验:

加深了自己对于泛型的理解和认识,提高了代码编写、ADT设计的能力。编写test测试文件时,有些方法的测试也能覆盖到其他的方法,避免重复测试增加工作量。

教训:

在设计多个类并使之互相配合的方面做得不好,编写代码的逻辑性有待提高。

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

前者代码复用性较好,避免大量重复工作,灵活性高,唯一劣势是ADT设计较难。直接面向应用场景逻辑更清晰,但是代码复用率低,重复工作多

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

可读性与可维护性更强,也更适合面向test编程

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

优势是不考虑代码的内部实现,只需考虑是否完成了规约中指定的功能。难以适应这种方式。

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

减少重复代码

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

提高程序的安全性与可读性,便于其他人阅读与维护,及客户端代码使用。虽然愿意但很多时候想不明白

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

工作量适中但是难度较大,如果ddl不延期又做不完

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

放慢上课节奏,下调实验难度,或者多来点学时,实验学时可以少一点,反正代码都是课下完成

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值