软件构造lab2小结
实验感受
面向ADT的编程
面向ADT的编程可以对实际问题进行抽象,提供更好的代码服用能力。
直接面向应用场景编程简单直接,而面向ADT的编程需要更多抽象能力。
泛型的好处
泛型增加了代码的复用性,减少重复代码。可以在不同的软件包和程序中使用同一套代码。
也方便代码的改进和重构。
测试驱动开发的优势
先写测试可以帮助我们去思考需求;任务被分解成了一个个小部分,减轻了负担;测试覆盖完全,有利于提高代码质量。
为ADT撰写specification, invariants, RI, AF的意义
specification既可以表明类的作用,以便将来可以快速读懂代码,又可以在编码使提醒自己应该编写怎样的功能,严格按照预定实现。
在编程的过程中,时刻提醒自己要注意表示泄露等问题,可以减少bug,提高编程效率。
实验目标概述
本次实验训练抽象数据类型(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创建Maven项目。
实验题目
Poetic Walks
这个问题集的目的是练习设计、测试和实现抽象数据类型。
- 完善Graph接口类,并运用泛型的思想,拓展为泛型类
- 实现Graph接口类,以边和点两种方式实现接口
- 利用实现的Graph类,应用图的思想,实现GraphPoet类。如果输入的文本的两个单词之间存在桥接词,则插入该桥接词;若存在多个单一桥接词,则选取边权重较大者。
测试Graph<String>
测试add():
按照Graph中有的点,Graph中没有的点,不同长度的字符串来测试
测试set():
按照增加边、修改边、删除边的策略来测试。
测试remove():
按照Graph中有的点,Graph中没有的点的策略来测试
测试vertices():
按照空图、有一个点、有多个点的策略来测试
测试source():
按照空图、非空图;target在Graph中存在、Graph中不存在;sources.size()为 0,1,>1的策略来测试
测试targets():
按照空图、非空图;source在Graph中存在、Graph中不存在;targets.size()为 0,1,>1的策略来测试
实现Graph<String>
Edge
类:
Edge中定义三个字段,分别代表边的源点,目标点,边权。
private final String source;
private final String target;
private final int weight;
表示不变性:source、target不能为空,权值weight>0
检查表示不变性:
private void checkRep() {
assert source != null;
assert target != null;
assert weight > 0;
}
ConcreteEdgesGraph
类
字段定义题目已给出:
private final Set<L> vertices = new HashSet<>();
private final List<Edge<L>> edges = new ArrayList<>();
```java
表示不变性:顶点不能为空,顶点不能重复,不能有重边,边的source和target包含于vertices
检查表示不变性:
```java
private void checkRep() {
for (L v : vertices)
assert v != null;
for (int i = 0; i < edges.size(); i++) {
Edge<L> e1 = edges.get(i);
assert vertices.contains(e1.getSource());
assert vertices.contains(e1.getTarget());
for (int j = i + 1; j < edges.size(); j++) {
Edge<L> e2 = edges.get(j);
assert !e1.getSource().equals(e2.getSource()) || !e1.getTarget().equals(e2.getTarget());
}
}
}
实现 ConcreteVerticesGraph
定义Vertex
类:存储一个顶点和此顶点的所有出边
定义两个私有字段,分别存储顶点的标签和此顶点的出边:
private final L label;
private final Map<L, Integer> targets = new HashMap<>();
表示不变性:label不为null,边权大于0
ConcreteVerticesGraph
类:
实现add():
将节点添加至列表中即可
if (findVertex(vertex) == null) {
vertices.add(new Vertex<>(vertex));
return true;
}
return false;
实现remove():
从列表中删除节点即可
Vertex<L> v = findVertex(vertex);
if (v == null) {
checkRep();
return false;
}
vertices.remove(v);
checkRep();
return true;
实现泛型Graph<L>
将各类型改为泛型类
public class ConcreteEdgesGraph<L> implements Graph<L>
class Edge<L>
public class ConcreteVerticesGraph<L> implements Graph<L>
class Vertex<L>
各个字段、函数返回值等都作相应修改
Poetic walk
测试GraphPoet
测试策略:
- graph: 空图、非空图
- 权值: 1,>1
- file:空文件、非空文件
实现GraphPoet
实现poem():
逐个读取单词,每两个单词之间插入亲和度最高的bridge单词
while (scanner.hasNext()) {
String now = scanner.next();
String bridge = null;
int maxAffinity = 0;
for (Map.Entry<String, Integer> entry1 : graph.targets(prev.toLowerCase()).entrySet()) {
for (Map.Entry<String, Integer> entry2 : graph.targets(entry1.getKey()).entrySet()) {
if (!entry2.getKey().equals(now)) continue;
if (entry1.getValue() + entry2.getValue() > maxAffinity) {
bridge = entry1.getKey();
maxAffinity = entry1.getValue() + entry2.getValue();
}
}
}
if (bridge != null) sb.append(" ").append(bridge);
sb.append(" ").append(now);
prev = now;
}
Re-implement the Social Network in Lab1
使用本次实验中编写的两种Graph之一重新实现即可