哈工大2020春软件构造实验二实验报告

在这里插入图片描述

2020年春季学期
计算机学院《软件构造》课程

Lab 2实验报告

姓名 赵俊
学号 1180300508
班号 1836101
电子邮件

手机号码

目录

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 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
3.3 Playing Chess 3
3.3.1 ADT设计/实现方案 3
3.3.2 主程序MyChessAndGoGame设计/实现方案 3
3.3.3 ADT和主程序的测试方案 3
4 实验进度记录 4
5 实验过程中遇到的困难与解决途径 4
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 实验环境配置
简要陈述你配置本次实验所需环境的过程,必要时可以给出屏幕截图。
特别是要记录配置过程中遇到的问题和困难,以及如何解决的。
安装Eclemma
Eclipse窗口点击help->Eclipse MarketPlace , 搜索EclEmma并安装;

在这里插入图片描述
出现此图标,表示eclemma安装成功

在这里给出你的GitHub Lab2仓库的URL地址(Lab2-学号)。
https://github.com/ComputerScienceHIT/Lab2-MikeSentence
3 实验过程
请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1 Poetic Walks
构建两个具体的有向图的类型,要求支持一系列操作:添加顶点;设置边和边权;删除顶点;删除边;给定一个点,寻找到达它的起点或寻找它能到达的终点;这两个图的类型分别以边存储、以顶点存储。自己写出测试样例,并通过测试
3.1.1 Get the code and prepare Git repository
Git init
Git clone https://github.com/rainywang/Spring2020_HITCS_SC_Lab2.git
复制P1文件夹下的所有文件即可
3.1.2 Problem 1: Test Graph
关于测试样例的写法并没有什么特别的,无非就写一个类似于客户端的代码,正常测试不同的等价类(负数,正数),异常测试等,下面只介绍empty写法与各测试文件的测试策略

①设置empty方法与重写emptyInstance方法
在这里插入图片描述

设置Graph类empty方法如上图
在这里插入图片描述

在ConcretEdgesGraphTest.java中重写emptyInstance方法如上,
在这里插入图片描述

在ConcretVerticesGraphTest.java中重写emptyInstance方法如上,

②测试策略
GraphInstanceTest.java类的测试策略:
在这里插入图片描述

Edge类的测试策略:
在这里插入图片描述

Vertex类的测试策略:
在这里插入图片描述

3.1.3 Problem 2: Implement Graph
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
由于是完成了整个实验才写实验报告,因此截图是最终的代码和注释,在3.1中L是String类型
3.1.3.1 Implement ConcreteEdgesGraph
class Edge:
在这里插入图片描述
在这里插入图片描述

如图为Edge类的AF与RI等说明

由于Edge是不可变类型,L是不可变类型,所有变量有final修饰符修饰,因此observer方法可以直接返回对应的变量
public String toString()
这里必须要重写toString方法,因为toString方法默认返回该对象的内存地址与内存的信息
返回的字符串表示出一条从source指向target的边权为weight的边这样的信息即可

如图为ConcreteEdgesGraph 类的AF,RI说明
在这里插入图片描述

class ConcreteEdgesGraph:
public boolean add(String vertex)
添加vertex到vertices之前判重即可

public int set(String source, String target, int weight)
遍历edges列表,根据weight值不同选择添加边或者删除找到的边,若需要添加顶点,调用add方法即可

public boolean remove(String vertex)
遍历vertices集合删除对应顶点元素,遍历edges列表删除包含此顶点的边
public Set vertices()
运用防御式拷贝策略,如图所示:
在这里插入图片描述

public Map<String, Integer> sources(String target)
首先遍历vertices列表判断有无target顶点,没有返回空,可以节约时间
若target顶点存在于vertices中,则遍历edges列表,删除关联此顶点的边

public Map<String, Integer> targets(String source)
与sources方法类似,不赘述

public String toString()
将此类所存信息生成字符串,自定义即可

3.1.3.2 Implement ConcreteVerticesGraph

class Vertex:
在这里插入图片描述
在这里插入图片描述

如图为Vertex的AF,RI等说明,我将每个顶点额外设置一个map来储存所有指向该点的边的起点与边权
如图为vertex内容一览
Vertex中L是不可变类型,其观察者方法getVertex可以直接返回vertex
Public int addSource(L source,int weight)
在sourcesMap中寻找source关键字,找到了,根据参数值修改对应的weight值或删除这对元素。若没找到,根据参数值决定添加关系还是不添加

public Map<L, Integer> getSourcesMap()
如图,进行防御性拷贝
在这里插入图片描述

public String toString()
返回此vertex的label以及它的sourcesMap信息

如图为ConcreteVerticesGraph 类的AF,RI说明
在这里插入图片描述
在这里插入图片描述

class ConcreteVerticesGraph:
public boolean add(String vertex)
添加vertex到vertices之前判重即可

public int set(String source, String target, int weight)
遍历Vertices列表寻找是否有target顶点:
若有target,根据weight值在target的sourcesMap中进行删除边或者添加边操作。
若无target,根据weight值决定是否新添加边和顶点到图中

public boolean remove(String vertex)
首先在vertices中遍历是否有vertex,若无,返回true,这样可以节约时间
若有vertex,将vertex从vertices删除,并且遍历vertices,从每个元素中含有vertex的sourcesMap删除vertex及其weight元素

public Set vertices()
防御式拷贝返回
在这里插入图片描述

public Map<String, Integer> sources(String target)
在vertices中寻找是否有target,若有,返回其sourcesMap,否则返回空map
public Map<String, Integer> targets(String source)
在vertices中寻找是否有source,若无,返回空map,否则,对所有vertices中的元素,遍历它们的sourcesMap,若source顶点在里边,添加这对元素到需要返回的map中即可
public String toString()
将此类所存信息生成字符串,自定义即可

3.1.4 Problem 3: Implement generic Graph
3.1.4.1 Make the implementations generic
将所有String类改为标签,Edge改为Edge,Vertex改为Vertex
3.1.4.2 Implement Graph.empty()
我选择用ConcreteEdgesGraph类来实现
这样写即可
在这里插入图片描述

3.1.5 Problem 4: Poetic walks
3.1.5.1 Test GraphPoet
这里仅给出测试策略,其实具体的也就是将main类的代码进行修改放到测试中
在这里插入图片描述

3.1.5.2 Implement GraphPoet
利用之前实现的Graph类的具体方法即可实现,将每个词视为一个顶点,该词的前后两词分别与该词构成构成有向边,顶点在前为起点。没什么难度
3.1.5.3 Graph poetry slam

3.1.6 Before you’re done
请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
git add xxx
git commit -m “xxx”
git push -u origin master
在这里给出你的项目的目录结构树状示意图。
在这里插入图片描述在这里插入图片描述

3.2 Re-implement the Social Network in Lab1
运用P1完成的ConcreteEdgeGraph类或ConcreteVertiecesGraph类来重构实验一的P3,尽可能运用P1的功能,摒弃原先的代码
3.2.1 FriendshipGraph类

在这里插入图片描述

如上声明以后,将Vertex,edge信息全部储存在graph里
基本写法和之前没有什么大的变化,代码结构是相同的。
3.2.2 Person类
在实验一的Person类代码基础上,重写了equals方法,用以比较人们两两之间的不同,两人相同当且仅当两人名字相同,与内存无关。toString方法返回此人的名字,用以打印信息
3.2.3 客户端main()
与实验一中P3相同
3.2.4 测试用例
在原来实验一中P3/test基础上增添了toString方法的测试
也增添了Person类的测试
3.2.5 提交至Git仓库
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
同上所述,不赘述
在这里给出你的项目的目录结构树状示意图。
在这里插入图片描述

3.3 Playing Chess
3.3.1 ADT设计/实现方案
设计了哪些ADT(接口、类),各自的rep和实现,各自的mutability/ immutability说明、AF、RI、safety from rep exposure。
必要时请使用UML class diagram(请自学)描述你设计的各ADT间的关系。
设计了Piece,Position,Player,Game,Board类,Action抽象类,和ChessAction、GoAction具体类(继承了Action)

①Position
在这里插入图片描述

②Player
在这里插入图片描述

③Piece
在这里插入图片描述

④Board
在这里插入图片描述

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

⑥Action

在这里插入图片描述

⑦GoAction

在这里插入图片描述

⑧ChessAction
在这里插入图片描述

3.3.2 主程序MyChessAndGoGame设计/实现方案
辅之以执行过程的截图,介绍主程序的设计和实现方案,特别是如何将用户在命令行输入的指令映射到各ADT的具体方法的执行。
设计方案:
以国际象棋游戏为例:
①获取游戏名
在这里插入图片描述

②获取玩家名字
在这里插入图片描述

③国际象棋菜单
在这里插入图片描述

④进行移动棋子操作
在这里插入图片描述

⑤进行吃子操作
在这里插入图片描述

⑥进行查询操作
在这里插入图片描述

⑦计算棋子数
在这里插入图片描述

⑧输入end结束游戏
在这里插入图片描述

⑨输入1查询历史记录
在这里插入图片描述

结束游戏
在这里插入图片描述

3.3.3 ADT和主程序的测试方案
介绍针对各ADT的各方法的测试方案和testing strategy。
介绍你如何对该应用进行测试用例的设计,以及具体的测试过程。
对于一个ADT,要进行不同等价类测试,这是一般的使用assertTrue、assertEquals与assertFalse方法即可。
进行异常测试时,需要使用try,catch语句捕捉异常,再利用assertEquals方法将异常信息与expected进行比较
测试覆盖率非常高
在这里插入图片描述

4 实验进度记录
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
日期 时间段 计划任务 实际完成情况
2020-03-20 18:30-19:30 编写P1的graphInstanceTest.java 遇到困难,未完成
2020-03-22 22:00-23:30 编写P1的graphInstanceTest.java 写完了代码,还有测试策略未完成
2020-03-26 13:30-15:30 编写P1问题的ConcreteEdgesGraph.java的代码部分 按时完成,还差注释
2020-03-27 15:30-17:30 修改P1中的graphInstanceTest.java与ConcreteEdgesGraph.java 遇到困难,未完成
2020-03-27 19:00-21:00 完成P1中的graphInstanceTest.java与ConcreteEdgesGraph.java与ConcreteEdgesGraphTest.java 按时完成,并提交至github
2020-03-28 10:00-13:00 编写P1中的ConcreteVerticesGraph.java及其测试文件 遇到困难,未完成
2020-03-28 18:30-20:30 编写P1的ConcreteVerticesGraph.java 按时完成
2020-03-29 10:00-12:00 修改P1的ConcreteVerticesGraph.java、ConcreteEdgesGraph.java 延期半小时完成
2020-03-30 18:30-20:30 修改P1的graphInstanceTest.java 按时完成
2020-03-31 20:00-22:00 完成P1的graph包及其测试包 按时完成
2020-04-01 20:00-23:00 编写P1的GraphPoetTest.java文件并测试 延期一个小时完成
2020-04-02 14:00-15:30 编写P2的Person.java文件与PersonTest.java文件 延期半小时完成
2020-04-02 16:00-17:00 编写P2的FriendshipGraph.java文件 遇到困难,未完成
2020-04-02 18:00-19:30 继续编写P2的FriendshipGraph.java及其测试文件 延期半小时完成
2020-04-05 14:00-22:00 编写P3 Position类,Piece类, 按时完成
2020-04-06 18:30-20:30 编写P3 Player类,Board类, 按时完成
2020-04-07 18:30-20:30 编写P3 Board类,Action类 按时完成
2020-04-08 14:00-17:00 编写P3 GoAction类,ChessAction类 遇到困难,未完成
2020-04-08 20:00-24:00 完成P3 GoAction类,ChessAction类,修改之前的Piece,Board,Player,Position类,完善功能 延期两小时完成
2020-04-09 12:00-15:00 编写P3 Game类 遇到困难,未完成
2020-04-09 18:00-20:00 完成P3 Game类 按时完成
2020-04-09 21:00-23:00 编写P3 MyChessAndGoGame类 遇到困难,未完成
2020-04-10 12:00-24:00 编写P3所有测试类并完善之前编写的所有类的功能,测试通过 按时完成
2020-04-11 14:00-16:00 完成MyChessAndGoGame类 延期一小时完成
5 实验过程中遇到的困难与解决途径
遇到的难点 解决途径
不会泛型语法
不懂接口定义与使用方法
不会抽象类抽象方法的使用 菜鸟教程一一学习
不知道如何写测试样例
不会可变类型与不可变类型 PPT一一学习
不会写抽象函数、表示不变量、测试策略、注释 PPT一一学习
toString与equals方法重写失败 百度学习别人是怎么写的
6 实验过程中收获的经验、教训、感想
6.1 实验过程中收获的经验和教训
不能直接用简单定义的Person类去使用,必须要重写equals方法,否则会出现该判断相同的结果是false

空指针不能调用方法,否则会出现NULLPointerException异常
判断一个对象是否为null应该用==而不是equals方法

Map与set的toString方法在不同时期测试结果中元素顺序会不一样,因此测试一次成功不能说明什么,同样,测试一次失败也不能说明错误

重写的toString方法在P1里调用正常,在P2中却无法正常调用,调用以后打印的是Person类的内存。原因是:Person类没有重写toString方法

针对以下方面的感受 (1) 面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异? ADT可以未雨绸缪,写出与功能相关的代码。而具体场景应用其具体实现方法,可以大大降低工作量,比起直接面向应用场景代码也不会显得赘余 (2) 使用泛型和不使用泛型的编程,对你来说有何差异? 泛型可以使应用场景多样化,不使用泛型比如写一个max函数,不使用泛型就要重载它很多次,而使用泛型或许就可以只写一次 (3) 在给出ADT的规约后就开始编写测试用例,优势是什么?你是否能够适应这种测试方式? 优势是在一边实现具体类时可以一边测试,利于修改。 在慢慢适应,毕竟之前一直是写好具体方法再进行测试,要改变做法需要时间 (4) P1设计的ADT在多个应用场景下使用,这种复用带来什么好处? 减小工作量,提高工作效率 (5) P3要求你从0开始设计ADT并使用它们完成一个具体应用,你是否已适应从具体应用场景到ADT的“抽象映射”?相比起P1给出了ADT非常明确的rep和方法、ADT之间的逻辑关系,P3要求你自主设计这些内容,你的感受如何? 万事开头难,不知道如何处理这些ADT的关系,谁是底层类?谁应该是abstract类?谁应该是接口?头脑中缺少spec,测试用例也是之后才写的而不是一开始就写的。 在尝试写出第一个abstract类Action后,开始有了个大概。在基本完成前,也发现很多不必要的传参操作,进而删除了它们,减少了测试量 (6) 为ADT撰写specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后编程中坚持这么做? 意义在于让开发者看懂自己写的东西,除了便于与别人交流,更重要的是让自己在开发的时候防止自己脑袋迷糊忘记自己该干什么,这个变量是拿来干嘛的,-1,1,0三个数字表示什么状态。忘了就倒回去看自己写的规约,大大减少无谓思考的时间。 愿意在以后编程中坚持这么做。 (7) 关于本实验的工作量、难度、deadline。 工作量比较大,难度适中,ddl设置十分合理。 (8) 《软件构造》课程进展到目前,你对该课程有何体会和建议? 本课程教我们如何规避一些不必要的麻烦,开发出高质量的软件,也让我们与其他程序员之间建立起规约,可以与其他人进行更好地交流