软件构造实验LAB2实验报告

2021年春季学期
计算学部《软件构造》课程

Lab 2实验报告
 

姓名

刘昕

学号

1190201116

班号

1903006

电子邮件

2914907975@qq.com

手机号码

16725341728

 

目录

1 实验目标概述···· 1

2 实验环境配置···· 1

3 实验过程···· 2

3.1 Poetic Walks· 2

3.1.1 Get the code and prepare Git repository· 2

3.1.2 Problem 1: Test Graph <String>· 2

3.1.3 Problem 2: Implement Graph <String>· 3

3.1.3.1 Implement ConcreteEdgesGraph· 3

3.1.3.2 Implement ConcreteVerticesGraph· 3

3.1.4 Problem 3: Implement generic Graph<L>· 4

3.1.4.1 Make the implementations generic· 4

3.1.4.2 Implement Graph.empty()· 4

3.1.5 Problem 4: Poetic walks· 4

3.1.5.1 Test GraphPoet· 4

3.1.5.2 Implement GraphPoet· 4

3.1.5.3 Graph poetry slam·· 5

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

3.1.7 Before you’re done· 5

3.2 Re-implement the Social Network in Lab1· 6

3.2.1 FriendshipGraph类···· 6

3.2.2 Person类···· 7

3.2.3 客户端main()· 7

3.2.4 测试用例···· 7

3.2.5 提交至Git仓库···· 8

4 实验进度记录···· 8

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

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

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

6.2 针对以下方面的感受···· 9

  1. 实验目标概述

本次实验训练抽象类型(ADT)的设计、规约、测试,并使用面向对象编程(OOP)技术实现ADT。具体来说:

针对给定的应用问题,从问题描述中识别所需的ADT;

设计ADT的规约并评估规约的质量

根据ADT的规约设计测试用例

ADT的泛型化

根据规约设计ADT的多种不同的实现;针对每种实现,设计其表示、表示不变性、抽象过程

使用OOP实现ADT,并判定表示不变性是否违反、各实现是否存在表示泄露

测试ADT的实现并评估测试的覆盖度

使用ADT及其实现,为应用问题开发程序

在测试代码中,能狗写出testing strategy 并据此设计测试用例

  1. 实验环境配置

打开help->Eclipse Marketplace 找到EclEmma然后点击安装即可。

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

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

  1. 实验过程

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

    1. Poetic Walks

设计抽象数据类型(ADT)Graph()的两个实现类,使用这个设计好的ADT完成Poetic Walks任务,并给出对应的测试类判断实现的正确性。

      1. Get the code and prepare Git repository

在github上下载代码并解压,文件夹命名为Lab2-1190201116

在文件夹中打开git bash 输入git init命令完成仓库创建

使用git remote add lab2 https://github.com/ComputerScienceHIT/HIT-Lab2-1190201116.git命令用lab2代替仓库网址。

      1. Problem 1: Test Graph <String>

·保留GraphStaticTest.java中的内容

·在GraphInstanceTest.java中写出测试策略并实现测试方法

在实例测试类中创建一个空对象,对5个需要重写的方法分别给出测试方法。

testAdd方法向图中增加3个不同的顶点1,2,3,预期返回true,然后向图中增加上述3个顶点中的1,预期返回false

testSet方法向图中增加三条不同的边,预期返回结果为0,然后向图中增加三条边中的一条,将权重进行修改,预期返回为刚才设置的权重

testRemove方法向图中增加一个顶点,然后删除这个刚增加的顶点,预期返回结果为true,然后删除一个不存在的顶点,预期返回结果为false

testVertices方法创建一个新的图对象,然后返回这个对象的顶点集,预期返回顶点集中元素个数为0,然后向其中添加一个顶点,再次返回这个对象的顶点集,预期返回顶点集中元素个数为1.

testSources方法向图中增加一个顶点a,这个新增加的顶点不和任何一个其他顶点相连,然后返回这个顶点的来源顶点和权重组成的图,预期图中不存放任何内容。接着再增加一个顶点b并且增加一条由b指向a的边,再次返回来源图,预期图中存放1条数据。

testTarget方法向图中增加一个顶点a,这个新增加的顶点不和任何一个其他顶点相连,然后返回这个顶点的目标顶点和权重组成的图,预期图中不存放任何内容。接着再增加一个顶点b并且增加一条由a指向b的边,再次返回目标图,预期图中存放1条数据。

      1. Problem 2: Implement Graph <String>

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

        1. Implement ConcreteEdgesGraph

首先实现Edge()类,Edge类型要求是不可变类型的。一个边有一个起点,一个终点,并且和这条边的权重,这些变量都是不可变类型的,并且使用private和final修饰,确保Edge类是不可变的。

在ConcreteEdgesGraphTest.java中写对Edge的测试类,testGet()方法测试Edge的三个返回变量的方法,testEquals()方法测试equals方法,testToStingForEdge()测试toString方法。

Edge类中设计实现多个方法,getStart()\getEnd()\getWeight()三个方法返回边的三个变量。不可变类型要求构造函数即需确定对象的三个变量,equals方法对两条边的三个变量分别比较,如果三个变量完全一样则返回true,否则返回false. toString方法返回一个包含起点、终点、权重的字符串。

       在ConcreteEdgesGraphTest.java中设计对toString()方法的测试策略,new一个图对象,在图中加入两个顶点和一条边,然后与预期输出字符串进行比对。

       在ConcreteEdgesGraph.java中重写Graph中的方法,并重写toString()方法。

        1. Implement ConcreteVerticesGraph

首先实现Vertex,Vertex要求是可变类型,field里面有一个不可变类型变量name,存储顶点的名称,两个HashMap分别存储这个顶点作为source和target,这两个量作为可变类型的,需要进行暴露保护。

checkRep()函数对顶点的来源边和终点边进行检查。

对这个顶点可以get顶点的名称和它的各个连接边,并且可以检查相对应的权重。顶点需要能够增加边的方法,并且分别为增加作为边起点的方法addEdgeAsSource()和作为边终点的方法addEdgeAsTarget()。equals()方法用于判断两个顶点是否相等,按照顶点的名称判断两个顶点是否相等。removeEdges()方法将当前顶点中所有带需要删除的顶点的边全部删除。toString()方法按照顶点名称和顶点的邻接边的形式输出顶点。

在测试类中加入对Vertex类的测试和对toString方法的测试,基本上与Edge类的测试方法相同。

ConcreteVerticesGraph class的field中只有一个顶点的列表,列表本身属于可变类型,因此必须要进行防御型复制来避免用户对其进行修改。接下来重写Graph中的方法和toString()方法。

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

将两个实现类中的所有String改为L,并将Vertex和Edge中和其测试类中的String改为泛型L

        1. Implement Graph.empty()

使Graph.empty()返回ConcreteEdgesGraph<>();

      1. Problem 4: Poetic walks
        1. Test GraphPoet

通过读文件将自己准备好的词库读入程序,语料库:you can do anything you want if you really want it.

输入字符串为you do anything you want.

期望输出字符串为you can do anything you really want.

        1. Implement GraphPoet

类中实现两个方法,一个是构造方法,要求在方法中读入文件并对文件进行处理,将信息放置在图中,完成整个对象的初始工作。

将相邻接的单词用权重为1的边连接起来,如果这条边已经存在,那么在原来权重基础上加1,并且判断之后的单词有没有在这个图中,如果没有需要先添加这个顶点到图中再修改边的情况。

第二个方法是生成诗歌的poem()方法,存入参数为输入的初始的字符串,首先将完整的句子进行拆分分解成一个个单词,然后在图中查找这两个单词,如果没有,则进行下一对的判断,如果有则调用图的target()和source()方法,获得前一个单词的target()和后一个单词的source(),如果两个词中间可以插入其他单词,则这两个HashMap中一定有相同的单词。再选出权重最高的单词即可。

        1. Graph poetry slam

不更改main()函数中的代码,运行即可。

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

      1. Before you’re done

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

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

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

Lab2-1190201116

       src

              P1

                     graph

                            ConcreteEdgesGraph.java

                            ConcreteVerticesGraph.java

                            Graph.java

                     Poet

                            GraphPoet.java

                            Main.java

                            Mugar-omni-theater.txt

       test

              P1

                     graph

                            ConcreteEdgesGraphTest.java

                            ConcreteVerticesGraphTest.java

                            GraphInstanceTest.java

                            GraphStaticTest.java

                     poet

                            GraphPoetTest.java

                            seven-words.txt

                    

    1. Re-implement the Social Network in Lab1

利用P1中实现的ADT重新实现FriendshipGraph类,要求功能完全一样,所以之前的测试函数和main函数仍然可以使用。

      1. FriendshipGraph

FriendshipGraph类继承自ConcreteEdgesGraph类,因此添加Person对象和增加边可以直接用父类中的add方法和set方法。在获得两个人间的最短距离时,依然采用广度优先搜索算法,首先将来源对象压入队列中,然后每弹出一个对象,返回他的终点边,然后判断终点边中是否有终点的Person对象,如果有返回距离,如果没有则将这个顶点压入队列中,并记录这个顶点,重复的顶点不会被压入队列中。如果最后队列被清空了还是没有这个顶点,则返回-1;如果source和target是两个相等的对象,则返回0.

      1. Person

Person类中只有一个name的字符串型变量,故Person是不可变类型。构造函数要求在一开始给定name的值,getName()方法返回对象的name,equals()方法通过判断两个对象的name是否相等来判断两个对象是否相等,如果相等返回true,否则返回false

      1. 客户端main()

Main函数仍然使用Lab1中给定的main函数

public static void main(String[] args) {

    FriendshipGraph graph=new FriendshipGraph();

    Person rachel=new Person("Rachel");

    Person ross=new Person("Ross");

    Person ben=new Person("Ben");

    Person kramer=new Person("Kramer");

    graph.addVertex(rachel);

    graph.addVertex(ross);

    graph.addVertex(ben);

    graph.addVertex(kramer);

    graph.addEdge(rachel,ross);

    graph.addEdge(ross,rachel);

    graph.addEdge(ross,ben);

    graph.addEdge(ben,ross);

    System.out.println(graph.getDistance(rachel, ross));

//should print 1

    System.out.println(graph.getDistance(rachel, ben));

//should print 2

    System.out.println(graph.getDistance(rachel, rachel));

//should print 0

    System.out.println(graph.getDistance(rachel, kramer));

//should print -1

}

      1. 测试用例

测试用例仍然使用Lab1中的测试函数。

      1. 提交至Git仓库

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

git add 命令加入暂存区,然后git commit 命令提交到本地库

通过git push命令上传至github远程库。

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

Lab2-1190201116

       src

              P2

                     FriendshipGraph.java

                     Person.java

       test

              P2

                     FriendshipGraphTest.java

  1. 实验进度记录

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

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

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

日期

时间段

计划任务

实际完成情况

6.1

16:00-18:30

ADT problem1 problem2.1

按时完成

6.1

20:00-21:30

ADT problem2.2

按时完成

6.2

16:00-17:00

ADT problem3.1

按时完成

6.2

20:10-21:30

ADT problem3.2

提前30分钟完成

6.3

15:00-18:00

ADT problem4

按时完成

6.7

14:00-16:00

Problem2

提前30分钟完成

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

遇到的难点

解决途径

不懂得如何检查不变性

通过查阅网上相关资料掌握如何写checkRep()方法并且在什么情况下使用

对于可变类型的数据不知道如何避免暴露

通过查阅资料和看ppt,可变类型数据可以返回一个复制的版本,而不可变类型数据可以直接返回。

映射关系处理不妥当

通过画图等方式理清映射关系并形成文字。

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

对类和接口的理解更加深刻,理解了继承的含义。

对泛型有了初步的认识

对OOP和ADT编程更加熟练,同时对课堂内容的理解更加深刻。

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

面向ADT编程可以直观的对于ADT的各种功能进行编程,不需要先考虑应用场景,等到实现了ADT后再在具体的场景使用它即可。并且代码的可移植性更高。

面向应用场景编程使用场景单一,但是可以更加贴向具体问题进行编程,有时候会简化代码,但是代码的可移植性差。

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

使用泛型可以有更多的使用形式,而不需要单纯限制一种数据类型。而不使用泛型,应用场景仍然较为单一。

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

给出规约后,相当于类的各种方法和功能都已经固定了,此时写测试程序正好针对写的规约进行测试,在写完程序后运行测试程序,可以检查刚才实现的方法有没有完全按照规约进行完成。

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

简化代码量,同时使编程更高效

  1. P3要求你从0开始设计ADT并使用它们完成一个具体应用,你是否已适应从具体应用场景到ADT的“抽象映射”?相比起P1给出了ADT非常明确的rep和方法、ADT之间的逻辑关系,P3要求你自主设计这些内容,你的感受如何?
  2. 为ADT撰写specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后编程中坚持这么做?

防止rep暴露是很重要的事情,如果一个对象的数据被暴露出来,并且可以被用户随意修改,那么程序就会出现很多错误并且难以发现。在今后的编程中,依然需要严格判断类中的每个数据是可变类型还是不可变类型,做好防止暴露的操作。

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

工作量适中,难度较低,能够在比较短的时间内完成。

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

该课程介绍了ADT\OOP等编程思想和编程方法,从一些细节角度讲了工程上如何做到安全性,教导如何使用面向对象的思想编程。希望这门课能多开设一些习题课,并且增加一些学时,讲述面向对象部分的时候能够更细致一些。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SZn..

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值