欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

软件构造Lab2

程序员文章站 2022-03-10 14:49:20
...

实验二

个人博客url 软件构造lab2

实验目标概述

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

实验环境配置

环境配置:
IntelliJ IDEA 2020.3.1 (JDK 1.8 Junit 4.12(下载到lib目录中))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AdcXz6B3-1624860192645)(/SoftwareConstruction/experiment2/image002.png)]

GitHub Lab2 URL:
github url

实验过程

Poetic Walks

Get the code and prepare Git repository

使用git clone命令从远程仓库中获取源代码

git clone https://github.com/rainywang/Spring2020_HITCS_SC_Lab2/tree/master/P1

并在本地使用git init命令建立git仓库。

通过git add . 命令添加所有文件,在通过git commit -m “message”的命令,提交本地项目,通过git remote add origin <url>的命令关联远程仓库,并使用git push -u origin master的命令,将其推送到远程github仓库。

我们也可以通过git log命令,详细查看提交记录,可以查看到不同版本。

总结:我们可以通过git status查看状态,git add . 添加文件,git commit -m “描述信息”生成版本控制,git log查看不同版本。

Problem 1: Test Graph <String>

  • 1> public boolean add(L vertex);

设计:我们此时根据图是否为空,待添加顶点是否在图中作为输入的区分,因此我们应该对这几种情况的笛卡尔积做测试。将此时分为三种情况:

图为空,则vertex不在图中

图不为空,且vertex不在图中

图不为空,且vertex在图中

思路、过程:根据方法上方的注释分析,此时我们需要添加一个顶点到这个图,若该图中没有包含该顶点,则返回true,否则我们应该返回false。因此,对于这种情况,我们应该测试输入作如下分区:图是否为空图,当前顶点是否在图中。并对这两种情况做笛卡尔积,排除掉图为空,且当前顶点在图中的情况。

结果:分别对三种情况做测试,测试通过。

  • 2> public int set(L source, L target, int weight);

设计:此时根据图是否为空,当前的边是否在图中,当前的顶点是否在图中,以及weight是否为0分区。对这些分区求笛卡尔积做测试,由于分区的笛卡尔积存在某些不可能出现的情况,故此时分为以下几种情况:

顶点在图中,边不在图中,图不为空,weight=0

顶点不在图中,边不在图中,图为空,weight=0

顶点在图中,边在图中,图不为空,weight=0

顶点在图中,边在图中,图不为空,weight!=0

顶点在图中,边不在图中,图不为空,weight!=0

顶点不在图中,边不在图中,图为空,weight!=0

思路、过程:根据方法上方的注释分析,我们需要添加,改变,或删除一个加权有向边在这个图中,如果weight!=0,则添加一条边,或更新该边的权值。若给定标签的顶点没有出现,则带有给定的标签的顶点将被添加到图中,若weight==0,且存在这条边,则去除这条边,返回之前边的权值。因此我们可以据此,对图是否为空,当前的边是否在图中,当前的顶点是否在图中,以及weight是否为0分区

结果:分别对以上几种情况测试,测试通过。

  • 3> public boolean remove(L vertex);

设计:此时可以根据当前顶点是否在图中进行分区,则一共分为两种。

当前顶点在图中

当前顶点不在图中

思路、过程:根据方法上方的注释分析,从图中去掉一个顶点,任何和这个顶点相关的边都删除。

结果:分别对以上几种情况测试,测试通过。

4> public Set<L> vertices();

设计:此时可以根据图是否为空,判断当前是否会返回值,因此分为以下两种情况。

图为空

图不为空

思路、过程:根据方法上方的注释分析,返回这个图中所有的顶点。

结果:分别对以上两种情况测试,测试通过。

5> public Map<L, Integer> sources(L target);

设计:此时可以根据顶点是否在图中,顶点是否存在源顶点分区。

顶点在图中,顶点存在源顶点

顶点不在图中,顶点不存在源顶点

顶点在图中,定点不存在源顶点

顶点不在图中,顶点存在源顶点

思路、过程:根据方法上方的注释分析,获取带有指向目标顶点的边的源顶点以及这些边的权值。因此我们可以根据当前顶点是否在图中,以及定点是否存在源顶点分区。

结果:分别对以上四种情况测试,测试通过。

6> public Map<L, Integer> targets(L source);

设计:此时可以根据顶点是否在图中,顶点是否存在目标顶点分区。

顶点在图中,顶点存在目标顶点

顶点不在图中,顶点存在目标顶点

顶点在图中,顶点不存在目标顶点

顶点不在图中,顶点不存在目标顶点

思路、过程:根据方法上方的注释分析,我们需要从源顶点获得带有有向边的目标顶点以及这些边的权值。因此我们可以对当前顶点是否在图中、当前顶点是否存在目标顶点分区。

结果:分别对以上四种情况测试,测试通过。

以下截图为在实现Problem2后对Problem1进行的测试:

依次通过ConcreteEdgesGraph与ConcreteVerticesGraph实现:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a1tkZJPM-1624860192646)(/SoftwareConstruction/experiment2/image004.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dZMM1low-1624860192647)(/SoftwareConstruction/experiment2/image005.png)]

Problem 2: Implement Graph <String>

Implement ConcreteEdgesGraph
  • 1> Edge类:

Edge类的私有变量如下,由于Edge类的变量都是用private、final修饰,且String、Integer均为不可变类型,因此Edge类不可变:

变量 意义
private final String source; 边的起点
private final String target; 边的终点
private final Integer weight; 边的权值

Edge类的方法如下:

方法 意义
public Edge(final String sourceConstruct,final String targetConstruct,final int weightConstruct) 构造函数
public void checkRep() 检查不变量,weight>0, source!=null, target!=null
public String getWeight () 获取边的weight值
public String getSource () 获取边的起点
public String getTarget() 获取边的终点
public String toString() 返回边的字符串表示
  • // Abstraction function:
  • // 从source到target,且权值为weight的有向边
  • // Representation invariant:
  • // weight>0,且source!=null,target!=null
  • // Safety from rep exposure:
  • // 使用private和final修饰变量,且使用变量的类型均为不可变类型。
  • // 避免从外部直接修改的风险。

CheckRep:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-inlaavWV-1624860192648)(/SoftwareConstruction/experiment2/image006.png)]

我们此时只需要检查this.weight>0,this.target!=null,this.source!=null,即可完成对不变量的检查。

toString:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oHZJkVAV-1624860192649)(/SoftwareConstruction/experiment2/image007.png)]

将toString函数打印方法设置如上,方便读取。

  • 2> ConcreteEdgesGraph类:

ConcreteEdgesGraph有两个私有变量:

变量 意义
private final Set<String> vertices = new HashSet<>() 图的所有顶点
private final List<Edge> edges = new ArrayList<>() 图的所有边

ConcreteEdgesGraph的方法如下:

方法 意义
public ConcreteEdgesGraph() 构造函数,创建一个空图
public void checkRep() 检查不变量
public boolean add(String vertex) 向图中添加一个边
public int set(String source, String target, int weight) 添加、该边、或删除一个图中的一个加权有向边
public boolean remove(String vertex) 去掉图中的一个顶点
public Set<String> vertices() 返回图中所有的顶点
public Map<String, Integer> sources(String target) 返回target顶点的所有源顶点及权值的映射
public Map<String, Integer> targets(String source) 返回source顶点的所有目标定点及权值的映射
public String toString() 返回图的字符串表示

继承关系如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vsd39x0p-1624860192650)(/SoftwareConstruction/experiment2/image008.png)]

  • // Abstraction function:
  • // 由顶点为vertices、边为edges组成的图
  • // 即从vertices、edges到有向图的映射
  • // Representation invariant:
  • // edges.getWeight>0,且edges中不存在两条相同的边
  • // vertices中的顶点不为空
  • // Safety from rep exposure:
  • // 使用private和final修饰变量
  • // 使用防御性复制

CheckRep:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mvLW1LGJ-1624860192650)(/SoftwareConstruction/experiment2/image009.png)]

检查edges中是否存在相同的边,edges中每条边的权值是否大于,以及是否存在顶点为空的情况。

toString:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ew9dVdI5-1624860192651)(/SoftwareConstruction/experiment2/image010.png)]

将toString函数打印方法设置如上,方便读取。

  • 3> 测试策略

对ConcreteEdgesGraph类的测试策略如下:

  1. public boolean add(String vertex)
    • // Testing s* trategy for ConcreteEdgesGraph.add()
    • //
    • // Partition the inputs as follows:
    • // whether the vertex is in the vertices
    • //
    • // Exhaustive Cartesian coverage of partitions.
  2. public int set(String source, String target, int weight)
    • // Testing strategy for ConcreteEdgesGraph.set()
    • //
    • // Partition the inputs as follows:
    • // whether the edge is in the graph
    • // weight : >0 =0
    • //
    • // Exhaustive Cartesian coverage of partitions.
  3. public boolean remove(String vertex)
    • // Testing strategy for ConcreteEdgesGraph.remove()
    • //
    • // Partition the inputs as follows:
    • // whether the vertex is in the graph
    • //
    • // Exhaustive Cartesian coverage of partitions.
  4. public Set<String> vertices()
    • // Testing strategy for ConcreteEdgesGraph.vertices()
    • //
    • // Partition the inputs as follows:
    • // whether the graph is empty
    • //
    • // Exhaustive Cartesian coverage of partitions.
  5. public Map<String, Integer> sources(String target)
    • // Testing strategy for ConcreteEdgesGraph.sources()
    • //
    • // Partition the inputs as follows:
    • // whether the target has sources
    • //
    • // Exhaustive Cartesian coverage of partitions.
  6. public Map<String, Integer> targets(String source)
    • // Testing strategy for ConcreteEdgesGraph.targets()
    • //
    • // Partition the inputs as follows:
    • // whether the source has targets
    • //
    • // Exhaustive Cartesian coverage of partitions.
  7. public String toString()
    • // Testing strategy for ConcreteEdgesGraph.toString()
    • //
    • // Partition the inputs as follows:
    • // whether the graph is empty
    • //
    • // Exhaustive Cartesian coverage of partitions.

对Edge类的测试策略如下:

  1. public String getWeight ()
    • // Testing strategy for Edge.getWeight()
    • //
    • // Partition the inputs as follows:
    • // Whether the weght is null
    • //
    • // Exhaustive Cartesian coverage of partitions.
  2. public String getSource ()
    • // Testing strategy for Edge.getSource()
    • //
    • // Partition the inputs as follows:
    • // Whether the source is null
    • //
    • // Exhaustive Cartesian coverage of partitions.
  3. public String getTarget()
    • // Testing strategy for Edge.getTarget()
    • //
    • // Partition the inputs as follows:
    • // Whether the target is null
    • //
    • // Exhaustive Cartesian coverage of partitions.
  4. public String toString()
    • // Testing strategy for Edge.toString()
    • //
    • // Partition the inputs as follows:
    • // Whether the Edge is empty
    • //
    • // Exhaustive Cartesian coverage of partitions.
  • 4> 测试结果:

可以看到所有测试通过,并且,代码覆盖率为100%.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2I2hK2Mf-1624860192651)(/SoftwareConstruction/experiment2/image011.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p9XVdsKL-1624860192652)(/SoftwareConstruction/experiment2/image012.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BOfjee7G-1624860192652)(/SoftwareConstruction/experiment2/image013.png)]

Implement ConcreteVerticesGraph
  • 1> Vertex类:

Vertex中含有两个私有变量:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F0os6Le2-1624860192652)(/SoftwareConstruction/experiment2/image014.png)]

以下为Vertex中的方法:

方法 意义
public Vertex(final String VertexName); 构造函数
public void checkRep(); 检查不变量
public boolean isEqualsVertex(Vertex vertexAnother); 检查顶点的名字是否与当前顶点名字相同
public boolean isEqualsName(String vertexAnother); 检查字符串是否与当前顶点名字相同
public void writeTarget(String vertexAnother, Integer weight); 将有向边写入TargetEdge中
public String Name(); 返回当前顶点的名字
public Map<String, Integer> Target(); 返回当前顶点所有目标顶点及对应权值
public Integer weight(String vertexAnother); 返回当前顶点所有源顶点及对应权值
public boolean remove(String vertexTarget); 去除掉当前顶点的vertexTarget目标顶点
public String toString(); 返回图的字符串表示
  • // Abstraction function:
  • // 由顶点的名字及其对应目标顶点的映射所对应的实际顶点表示
  • // 即由String、Map组成的抽象数据类型对应的顶点
  • // Representation invariant:
  • // vertex不为空且TargetEdge中的weight>0
  • // Safety from rep exposure:
  • // 使用private、final修饰的变量
  • // 防御性复制

CheckRep:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-09cxdBSs-1624860192653)(/SoftwareConstruction/experiment2/image015.png)]

检查vertex不为空且TargetEdge中的weight>0。

toString:

将toString函数打印方法设置如上,方便读取。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Df925TLm-1624860192653)(/SoftwareConstruction/experiment2/image016.png)]

2> ConcreteVerticesGraph类:

ConcreteVerticesGraph类有一个私有变量:

变量 意义
private final List<Vertex> vertices = new ArrayList<>() 存储所有顶点

以下为ConcreteVerticesGraph的方法:

方法 意义
public ConcreteVerticesGraph() 构造函数,创建新的空的图
public void checkRep() 检查不变量
public boolean add(String vertex) 将顶点添加到图中
public int set(String source, String target, int weight)
public boolean remove(String vertex) 从图中去掉vertex顶点
public Set<String> vertices() 返回图中所有顶点的集合
public Map<String, Integer> sources(String target) 返回target在图中的所有源顶点
public Map<String, Integer> targets(String source) 返回source在图中的所有目标顶点
public String toString() 返回图的字符串表示

继承关系如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2mtbIEb1-1624860192653)(/SoftwareConstruction/experiment2/image017.png)]

  • // Abstraction function:
  • // 由vertices组成的顶点
  • // 即由List<Vertex>类型数据到加权有向图的映射
  • // Representation invariant:
  • // vertices中不存在重复的顶点
  • // Safety from rep exposure:
  • // 使用private、final修饰变量
  • // 采用防御性复制

CheckRep:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XxR0j9dp-1624860192654)(/SoftwareConstruction/experiment2/image018.png)]

toString:

将toString函数打印方法设置如上,方便读取.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cFawmz0n-1624860192654)(/SoftwareConstruction/experiment2/image019.png)]

  • 3> 测试策略:

对ConcreteVerticesGraph的测试策略:

  1. public boolean add(String vertex)
    • // Testing strategy for ConcreteVerticesGraph.add()
    • //
    • // Partition the inputs as follows:
    • // 是否为空图: empty graph? yes 、no
    • // 当前的顶点是否在图中:whether vertex is in the graph? yes 、no
    • //
    • // Exhaustive Cartesian coverage of partitions.
  2. public int set(String source, String target, int weight)
    • // Testing strategy for ConcreteVerticesGraph.set()
    • //
    • // Partition the inputs as follows:
    • // 当前的顶点是否在图中:whether vertex is in the graph? yes 、no
    • // value of weight? =0、>0
    • // 顶点是否存在目标顶点:whether vertex has target? yes 、no
    • //
    • // Exhaustive Cartesian coverage of partitions.
  3. public boolean remove(String vertex)
    • // Testing strategy for ConcreteVerticesGraph.remove()
    • //
    • // Partition the inputs as follows:
    • // 是否为空图: empty graph? yes 、no
    • // 当前的顶点是否在图中:whether vertex is in the graph? yes 、no
    • //
    • // Exhaustive Cartesian coverage of partitions.
  4. public Set<String> vertices()
    • // Testing strategy for ConcreteVerticesGraph.vertices()
    • //
    • // Partition the inputs as follows:
    • // 是否为空图: empty graph? yes 、no
    • //
    • // Exhaustive Cartesian coverage of partitions.
  5. public Map<String, Integer> sources(String target)
    • // Testing strategy for ConcreteVerticesGraph.sources()
    • //
    • // Partition the inputs as follows:
    • // 是否为空图: empty graph? yes 、no
    • // 当前的顶点是否在图中:whether vertex is in the graph? yes 、no
    • // 顶点是否存在源顶点:whether vertex has source? yes 、no
    • //
    • // Exhaustive Cartesian coverage of partitions.
  6. public Map<String, Integer> targets(String source)
    • // Testing strategy for ConcreteVerticesGraph.targets()
    • //
    • // Partition the inputs as follows:
    • // 是否为空图: empty graph? yes 、no
    • // 当前的顶点是否在图中:whether vertex is in the graph? yes 、no
    • // 顶点是否存在目标顶点:whether vertex has target? yes 、no
    • //
    • // Exhaustive Cartesian coverage of partitions.
  7. public String toString()
    • // Testing strategy for ConcreteVerticesGraph.toString()
    • //
    • // Partition the inputs as follows:
    • // empty graph: yes 、no
    • //
    • // Exhaustive Cartesian coverage of partitions.

对Vertex类的测试策略如下:

  1. public boolean isEqualsVertex(Vertex vertexAnother)
    • // Testing strategy for Vertex.isEqualsVertex()
    • //
    • // Partition the inputs as follows:
    • // whether vertexAnother is equal to Vertex
    • //
    • // Exhaustive Cartesian coverage of partitions.
  2. public boolean isEqualsName(String vertexAnother)
    • // Testing strategy for Vertex.isEqualsName()
    • //
    • // Partition the inputs as follows:
    • // whether vertexAnother’s name is equal to Vertex
    • //
    • // Exhaustive Cartesian coverage of partitions.
  3. public void writeTarget(String vertexAnother, Integer weight)
    • // Testing strategy for Vertex.writeTarget()
    • //
    • // Partition the inputs as follows:
    • // whether vertexAnother is in the TargetEdge
    • // weight:>0 =0
    • //
    • // Exhaustive Cartesian coverage of partitions.
  4. public String Name()
    • // Testing strategy for Vertex.Name()
    • //
    • // whether Vertex is null
    • //
    • // Exhaustive Cartesian coverage of partitions.
  5. public Map<String, Integer> Target()
    • // Testing strategy for Vertex.Target()
    • //
    • // Partition the inputs as follows:
    • // wherger TargetEdge is null
    • //
    • // Exhaustive Cartesian coverage of partitions.
  6. public Integer weight(String vertexAnother)
    • // Testing strategy for Vertex.weight()
    • //
    • // Partition the inputs as follows:
    • // whether vertexAnother is in the TargetEdge
    • //
    • // Exhaustive Cartesian coverage of partitions.
  7. public boolean remove(String vertexTarget)
    • // Testing strategy for Vertex.remove()
    • //
    • // Partition the inputs as follows:
    • // vertexTarget is in the TargetEdge or not
    • //
    • // Exhaustive Cartesian coverage of partitions.
  8. public String toString()
    • // Testing strategy for Vertex.toString()
    • //
    • // Partition the inputs as follows:
    • // vertex is null or not
    • //
    • // Exhaustive Cartesian coverage of partitions.
  • 4> 测试结果

可以看到测试全部通过,且代码覆盖率为100%:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sHGKJCS5-1624860192654)(/SoftwareConstruction/experiment2/image020.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JRVpYSrT-1624860192655)(/SoftwareConstruction/experiment2/image021.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-maHaGzNR-1624860192655)(/SoftwareConstruction/experiment2/image022.png)]

Problem 3: Implement generic Graph<L>

Make the implementations generic

此时具体类的声明如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eeDdIZiq-1624860192656)(/SoftwareConstruction/experiment2/image023.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mVSW6C10-1624860192656)(/SoftwareConstruction/experiment2/image024.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4kdUFuN4-1624860192656)(/SoftwareConstruction/experiment2/image025.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sk5hcqHC-1624860192657)(/SoftwareConstruction/experiment2/image026.png)]

并对problem2部分实现的功能修改,使其支持泛型。

将ConcreteEdgesGraph及ConcreteVerticesGraph中部分用String声明的变量修改为L,并且在声明类的时候,使用泛型L。

实现后具体类的修改后,对其测试,发现测试全部通过。且覆盖率仍为100%

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j52SpnRW-1624860192657)(/SoftwareConstruction/experiment2/image027.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3XIEBufd-1624860192657)(/SoftwareConstruction/experiment2/image028.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rLyGsfwN-1624860192658)(/SoftwareConstruction/experiment2/image029.png)]

Implement Graph.empty()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iWWfDjln-1624860192658)(/SoftwareConstruction/experiment2/image030.png)]

此处选择ConcreteEdgesGraph或ConcreteVerticesGraph中的一个。

为了确保支持不同的类型,分别测试两种不同的类型Integer、Charater。

分别对不同的类型对实现的方法测试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MpRyWaG5-1624860192658)(/SoftwareConstruction/experiment2/image031.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-361IFTBU-1624860192659)(/SoftwareConstruction/experiment2/image032.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ui97GoFu-1624860192659)(/SoftwareConstruction/experiment2/image033.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ooRH4cwZ-1624860192659)(/SoftwareConstruction/experiment2/image034.png)]

如图,此时测试覆盖率为100%.

Problem 4: Poetic walks

Test GraphPoet

对GraphPoet类的测试如下:

  1. public GraphPoet(File corpus)
    • // Testing strategy for the constructor of GraphPoet
    • // Testing strategy for GraphPoet.toString()
    • // Partition the inputs as follows:
    • // whether the corpus is not exist
    • // whether the corpus is a empty file
    • //
    • // Exhaustive Cartesian coverage of partitions.
  2. public String poem(String input)
    • // Testing strategy for GraphPoet.poem()
    • //
    • // Partition the inputs as follows:
    • // Whether the input has the same edge
    • //
    • // Exhaustive Cartesian coverage of partitions.
  3. public String toString()
    • // Testing strategy for the constructor of GraphPoet
    • // Testing strategy for GraphPoet.toString()
    • // Partition the inputs as follows:
    • // whether the corpus is not exist
    • // whether the corpus is a empty file

在完成下一单元后,对其测试,测试结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BGSqE1fx-1624860192660)(/SoftwareConstruction/experiment2/image035.png)]

可以看到,此时覆盖率为百分值百:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nFHnRpwb-1624860192660)(/SoftwareConstruction/experiment2/image036.png)]

对部分测试策略分析:
当输入文件为下方文件时:
> To explore to explore interesting new interesting explore interesting explore strange new worlds
> To seek out new life and new civilizations and civilizations and

此时graph如图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9X9nJswK-1624860192660)(/SoftwareConstruction/experiment2/image037.png)]

当我们的输入语句为Seek to explore new and exciting synergies!时,此时对应的语句应该为Seek to explore interesting new civilizations and exciting synergies!
当此时输入文件为下方字符时:

> To explore strange new worlds
> To seek out new life and new civilizations

此时graph如图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4TSie2Y8-1624860192661)(/SoftwareConstruction/experiment2/image038.png)]

当我们的输入语句为Seek to explore new and exciting synergies!时,此时对应的语句应该为Seek to explore strange new life and exciting synergies!

Implement GraphPoet
    1. public GraphPoet(File corpus)

首先是文件的读取,采用System.getProperty(“user.dir”)的函数读取当前工作区,并加上路径,读取出所需的文件。

再通过BufferedReader、FileReader将文件读取到一个字符串中,再通过split函数,将所有空格消除,并读取到List中,再通过List中的removeAll函数,将所有的空字符串删除,此时即可得到文件中所有的单词,并保存到一个列表中。

依次对所有的单词遍历,若前一个节点及后一个节点都在图中,且weight++,否则令weight=1.

    1. public String poem(String input)

首先将input的输入通过split函数,切分为每个单词,并通过与GraphPoet相似的方法,获得单词的列表,此时我们用List中的顺序,每次取出第i个,及第i+1,如果第i个单词及第i+1个单词之间在图中存在别的单词连接,则将这个单词放到StringBuilder中。直到结束后,将最后一个单词也放进,并加上感叹号。

具体结构如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L5NtMrAK-1624860192661)(/SoftwareConstruction/experiment2/image039.png)]

Graph poetry slam

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L7Sfl741-1624860192661)(/SoftwareConstruction/experiment2/image040.png)]

对于如图所示的main函数,由于输入的文件为:
> This is a test of the Mugar Omni Theater sound system.

因此在test与the中间存在of单词,因此输出结果应该为:Test of the system!

通过测试,所得结果与预期一致,完成预期要求。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pEVeOsaO-1624860192662)(/SoftwareConstruction/experiment2/image041.png)]

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

由于IDE采用IntelliJ IDEA 2020.3.1,此处使用IDEA内置的使用覆盖率测试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hjGhGI34-1624860192662)(/SoftwareConstruction/experiment2/image042.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qnKZGE3b-1624860192663)(/SoftwareConstruction/experiment2/image043.png)]

可以看到此时覆盖率为100%。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bFOzXkZj-1624860192663)(/SoftwareConstruction/experiment2/image044.png)]

将工作区复制到Ecilipse中,用Eclemma检测覆盖率,此时覆盖率仍然大约为95%左右。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2SfI07bF-1624860192663)(/SoftwareConstruction/experiment2/image045.png)]

Before you’re done

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

git add .
git commit -m "P1"
git push

项目的目录结构树状示意图如下方所示:
此处采用linux中的tree命令.

Re-implement the Social Network in Lab1

FriendshipGraph类

  • 1.FriendshipGraph类有一个私有变量:
    private Graph<Person> graph = Graph.empty();
  • 2.FriendshipGraph类的方法如下:
方法 意义
public void checkRep() 检查不变量
public boolean addVertex(Person person) 添加顶点
public boolean addEdge(Person person1,Person person2) 添加边
public int getDistance(Person person1,Person person2) 返回从person1到person2的距离

关系图如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oy2krM45-1624860192664)(/SoftwareConstruction/experiment2/image046.png)]

  • // Abstraction function:
  • // 由Graph<Person> graph对应的图
  • // Representation invariant:
  • // graph中所有边的weight=0或=1,且顶点名字不为空
  • // Safety from rep exposure:
  • // 使用private、final修饰的变量
  • // 防御性复制

checkRep如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CSRjHWGp-1624860192664)(/SoftwareConstruction/experiment2/image047.png)]

getDistance的实现如下:

  • 1.将person1保存到HashSet<Person> Search中;
  • 2.将person1认识的人保存到HashSet<Person> Search_Next中;
  • 3.将Search和Search_Next都保存到新的HashSet<Person> Search_assist中;
  • 4.初始化distance = 1;
  • 5.若Search_Next中含有person2,返回distance,即为所求距离。否则将Search清空,将Search_Next保存到Search中,在将Search中所有的人认识的人的HashSet中所有的元素添加到Search_Next,再将Search_Next中所有在Search_assist中保存的元素消除掉,再将所有Search_Next元素添加到Search_assist中。此时distance++;
  • 6.重复步骤5,若Search_Next最终为空,则不存在距离,返回值为-1;否则,person1与person2之间存在路径,且 person1与person2距离为distance。

Person类

    1. Person类有一个私有变量:

private final String name;

    1. FriendshipGraph类的方法如下:
方法 意义
public Person(String personName) 构造函数
public void checkRep() 检查不变量
public String getName() 返回当前Person的姓名
  • // Abstraction function:
  • // 由String name对应的Person
  • // Representation invariant:
  • // name不为空
  • // Safety from rep exposure:
  • // 使用private、final修饰的变量
  • // 防御性复制

checkRep如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r6U8gTrR-1624860192664)(/SoftwareConstruction/experiment2/image048.png)]

客户端main()

此处将重新利用Lab1中main客户端,由于对addVertex、addEdge、getDistance该函数均已完成,因此此处重新利用即可。
运行发现,与预期结果一致。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s7xbhhdo-1624860192665)(/SoftwareConstruction/experiment2/image049.png)]

测试用例

对于FriendshipGraph类:

  1. 测试策略:

1.1 public boolean addVertex(Person person)

  • // Testing strategy for GraphPoet.addVertex()
  • //
  • // Partition the inputs as follows:
  • // Whether the grapg is empty
  • //
  • // Exhaustive Cartesian coverage of partitions.

1.2 public boolean addEdge(Person person1,Person person2)

  • // Testing strategy for GraphPoet.addVertex()
  • //
  • // Partition the inputs as follows:
  • // Whether the edge is in the graph
  • //
  • // Exhaustive Cartesian coverage of partitions.
    1.3 public int getDistance(Person person1,Person person2)
  • // Testing strategy for GraphPoet.getDistance()
  • //
  • // Partition the inputs as follows:
  • // Is it possible to go from A to B
  • //
  • // Exhaustive Cartesian coverage of partitions.
  1. 测试结果

    此处主要说明对getDistance的测试,其余部分详见代码。
    通过以下的图,对getDistance测试:
    由图可得,ben 与 a 的距离为 2,ben 与 kramer 的距离为 3,b 与 kramer的距离为 2,kramer 与 rachel 的距离为 5。
    并进行测试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bZ6L1QaA-1624860192665)(/SoftwareConstruction/experiment2/image050.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-56mQwnF1-1624860192666)(/SoftwareConstruction/experiment2/image051.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kownst5I-1624860192666)(/SoftwareConstruction/experiment2/image052.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BUF4zSDw-1624860192666)(/SoftwareConstruction/experiment2/image053.png)]

可以发现,此时测试全部通过测试,并且代码覆盖率为100%.

对于Person类:

  1. 测试策略
  • // Testing strategy for Person.getName
  • //
  • // Partition the inputs as follows:
  • // whether Person is null
  • //
  • // Exhaustive Cartesian coverage of partitions.
  1. 测试结果

测试全部通过,并且代码覆盖率100%。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AlVm82Jk-1624860192667)(/SoftwareConstruction/experiment2/image054.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FxPlFyIW-1624860192667)(/SoftwareConstruction/experiment2/image055.png)]

提交至Git仓库

如何通过Git提交当前版本到GitHub上你的Lab2仓库。
在这里给出你的项目的目录结构树状示意图。

git add .
git commit -m "P2"
git push

目录结构树状示意图如下所示:

此处采用linux中的tree命令.

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

遇到的难点 解决途径
泛型的转换最初理解起来稍有问难。 通过查阅资料并与同学讨论等途径解决。
对于题目某些部分理解起来稍有困难。 通过自己耐心探究解决。
测试策略的设计。 通过查阅相关资料并加以体会解决。

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

  • 1.掌握了在测试中,写出测试策略,并根据此设计测试用例的方法
  • 2.了解了OOP实现ADT,并对RI、REP、AF等有了更深的了解
  • 3.对于整个过程了解更加深刻
  • 4.掌握了ADT设计的基本方法
  • 5.了解了ADT的泛型化
  • 6.意识到避免表示泄露的重要性,并在实践中,践行了保护的过程

针对以下方面的感受

(1) 面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?

在面向ADT的编程设计,我们往往要考虑程序的功能、应用场景以及复用性,使编出的程序更加灵活;而面向应用场景编程,适用范围较小,但可以根据具体场景,灵活编程。

(2) 使用泛型和不使用泛型的编程,对你来说有何差异?

使用泛型可以更改数据类型,如P2可以以Person为数据类型,在初次接触时有一些不适应,但很快便了解并掌握。泛型为程序的复用性带来了很大帮助,并且使类型更加安全。

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

先完成测试用例的编写能够让我更好的理解规格说明。规格说明也可能存在问题–不正确、不完整、模棱两可、确实边界情况。 因此先尝试编写测试用例,可以在我浪费时间实现一个有问题的规格说明之前发现这些问题。因此我能够适应这种测试方式。

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

避免重复实现具有类似功能的ADT,将已有代码进行复用,不仅在一定程度上节约了时间,同时还能进一步保证程序的正确性,避免写类似代码时,犯更多的错误。同时还能够锻炼我们的抽象能力,在实现ADT时候,充分考虑应用场景。

(5) P3要求你从0开始设计ADT并使用它们完成一个具体应用,你是否已适应从具体应用场景到ADT的“抽象映射”?相比起P1给出了ADT非常明确的rep和方法、ADT之间的逻辑关系,P3要求你自主设计这些内容,你的感受如何?

实验未要求P3.

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

表示暴露影响程序的安全运行,同时影响到不变性和表示独立性处理表示暴露避免类内部数据被从外部访问。并且在编程时候在抽象类型表示声明后写上对于抽象函数和表示不变量的注解,可以便于读取表示不变量、抽象域的表示、规定合法的表示值如何被解释到抽象域。方便程序员的处理。以后也会坚持这个习惯。

(7) 关于本实验的工作量、难度、deadline。

本实验工作量较大,尤其是编写测试时,耗费时间较长,难度可以接受,deadline比较合理。

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

使我对软件构造有了新的感悟,一方面对程序的测试分区等有了新的体会,另一方面,对规范的注释等有了新的了解,另一方面更加体会到了面向对象编程,除此之外,更加了解了如何保证程序的安全运行。
建议,对PPT内容扩充,方便同学们复习。
序的复用性带来了很大帮助,并且使类型更加安全。

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

先完成测试用例的编写能够让我更好的理解规格说明。规格说明也可能存在问题–不正确、不完整、模棱两可、确实边界情况。 因此先尝试编写测试用例,可以在我浪费时间实现一个有问题的规格说明之前发现这些问题。因此我能够适应这种测试方式。

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

避免重复实现具有类似功能的ADT,将已有代码进行复用,不仅在一定程度上节约了时间,同时还能进一步保证程序的正确性,避免写类似代码时,犯更多的错误。同时还能够锻炼我们的抽象能力,在实现ADT时候,充分考虑应用场景。

(5) P3要求你从0开始设计ADT并使用它们完成一个具体应用,你是否已适应从具体应用场景到ADT的“抽象映射”?相比起P1给出了ADT非常明确的rep和方法、ADT之间的逻辑关系,P3要求你自主设计这些内容,你的感受如何?

实验未要求P3.

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

表示暴露影响程序的安全运行,同时影响到不变性和表示独立性处理表示暴露避免类内部数据被从外部访问。并且在编程时候在抽象类型表示声明后写上对于抽象函数和表示不变量的注解,可以便于读取表示不变量、抽象域的表示、规定合法的表示值如何被解释到抽象域。方便程序员的处理。以后也会坚持这个习惯。

(7) 关于本实验的工作量、难度、deadline。

本实验工作量较大,尤其是编写测试时,耗费时间较长,难度可以接受,deadline比较合理。

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

使我对软件构造有了新的感悟,一方面对程序的测试分区等有了新的体会,另一方面,对规范的注释等有了新的了解,另一方面更加体会到了面向对象编程,除此之外,更加了解了如何保证程序的安全运行。
建议,对PPT内容扩充,方便同学们复习。