301
社区成员
发帖
与我相关
我的任务
分享本次代码架构主要依照官方包提供的JML语言描述的接口进行实现。
主要以Network组织Person, Tag, Message类实现一个模拟的社交网络。
JML的描述有效为抛出异常提供了参考,也明确了方法的目的,避免了在方法名、类名以及问题文字描述中出现了歧义问题,保证了代码的表意严谨清晰。
其中,不限于JML实现的,个人实现了NetMap类来对Network中个人及其关系进行存贮和对相关方法进行优化实现。
内含并查集的构建
private void init() {
blockNum = 0;
for (Integer id : persons.keySet()) {
father.put(id, id);
rank.put(id, 1);
blockNum++;
}
}
private int find(int id) {
if (father.get(id) == id) {
return id;
}
else {
father.put(id, find(father.get(id)));
return father.get(id);
}
}
private void merge(int id1, int id2) {
int fa1 = find(id1);
int fa2 = find(id2);
if (rank.get(fa1) <= rank.get(fa2)) {
father.put(fa1, father.get(fa2));
}
else {
father.put(fa2, father.get(fa1));
}
if (Objects.equals(rank.get(fa1), rank.get(fa2)) && fa1 != fa2) {
rank.put(fa2, rank.get(fa2) + 1);
}
if (fa1 != fa2) {
blockNum--;
}
}
以及shortestPath的BFS实现
public int shortestPath(int id1, int id2) {
if (id1 == id2) {
return 0;
}
HashMap<Integer, Integer> visited = new HashMap<>();
LinkedList<Integer> queue = new LinkedList<>();
queue.addLast(id1);
visited.put(id1, 0);
while (!queue.isEmpty()) {
int cur = queue.getFirst();
int dis = visited.get(cur);
queue.removeFirst();
MyPerson person = (MyPerson) persons.get(cur);
for (Person acq : person.getAcquaintance().keySet()) {
int id = acq.getId();
if (!visited.containsKey(id)) {
if (id == id2) {
return dis;
}
queue.addLast(id);
visited.put(id, dis + 1);
}
}
}
return -1;
}
再代码架构中对人关系图的构建中,个人采用查并集进行对连通情况的储存,并利用脏位进行维护,在脏位置1时进行查并集的重建。而对其他方法则采用了动态维护。
###性能问题及其修复情况
本次作业生动体现了规格与实现分离的理念。本次作业中规格的描述只规范了方法的目的与过程中的保障。
而采用了实际上相当朴素的实现描述。
如以下关于queryTripleSum的描述。
/*@ ensures \result ==
@ (\sum int i; 0 <= i && i < persons.length;
@ (\sum int j; i < j && j < persons.length;
@ (\sum int k; j < k && k < persons.length
@ && getPerson(persons[i].getId()).isLinked(getPerson(persons[j].getId()))
@ && getPerson(persons[j].getId()).isLinked(getPerson(persons[k].getId()))
@ && getPerson(persons[k].getId()).isLinked(getPerson(persons[i].getId()));
@ 1)));
@*/
而实际实现中个人采用了动态维护方法,在每次增删人际关系时对triplesum进行修改,防止被强测压力测试刷下。
同样,个人在queryTagValueSum等方法中同样用动态维护实现性能上的优化。
本单元要求实现Junit对某几个方法进行测试。在各次Junit中都需要优先构造出一个图,并在之后进行指令测试。
而在各个方法的测试中,可依据JML提供的相应描述进行实现。
将require与ensure语句转化为assert,对方法执行前后条件进行判断。
对pure与assignable等语句转化为对方法前后对应数据的变化进行断言,保障前后数据该变的变,不该变的不变。
按照JML编写Junit有效保障了方法按照JML的正确实现。
个人在Junit中实现了对Person,Relation,Emoji的对应容器进行了深克隆,并与方法后进行比对,确保对应数据一致性。同时为了通过中测,在构造图中涵盖稠密图、稀疏图并利用多次测试,增加测试强度。
对于各个方法。
###黑箱与白箱
黑盒测试是从一种从软件外部对软件实施的测试,也称功能测试或基于规格说明的测试。其基本观点是:任何程序都可以看作是从输入定义域到输出值域的映射,这种观点将被测程序看作一个打不开的黑盒,黑盒里面的内容(实现)是完全不知道的,只知道软件要做什么。因无法看到盒子中的内容,所以不知道软件是如何实现的,也不关心黑盒里面的结构,只关心软件的输入数据和输出结果.
白盒测试是知道产品内部工作过程,可通过测试来检测产品内部动作是否按照规格说明书的规定正常进行,按照程序内部的结构测试程序,检验程序中的每条通路是否都有能按预定要求正确工作,而不顾它的功能。
###各种测试
单元测试,对软件的组成单位进行测试,其目的是检验软件基本组成单位的正确性。测试的对象是软件里测试的各个最小单元。
功能测试是针对系统的功能进行测试的,例如用户界面、数据输入和输出、业务逻辑等。功能测试的目的是确保系统能够按照需求规格说明书中的要求正常运行,从而满足用户的需求。
集成测试则将程序模块采用适当的集成策略组装起来,对系统的接口及集成后的功能进行正确性检测的测试工作。集成主要目的是检查软件单位之间的接口是否正确。
压力测试是通过不断向被测系统施加“压力”,测试系统在压力情况下的性能表现,考察当前软硬件环境下系统所能承受的最大负荷并帮助找出系统瓶颈所在。
回归测试是在发现并修改缺陷后,或在软件中添加新的功能后,重新测试。用来检查被发现的缺陷是否被改正,并且所做的修改没有引发新的问题。
###数据构造
在前两次作业中,主要以构造大量数据指令下的压力测试为主。如构建出稠密图并在其上堆砌如qbs qts qtvs等与修改Relation和Tag的指令,以此来挑战的代码的性能。
而在最后一次测试中主要以混合不同Message的增添,dce进行测试,还有对redEnvelope的money除零问题进行测试。
本单元是OO目前最为轻松的一单元,大部分工作都是以按照JML进行翻译进行,最后一次作业中长篇幅的JML语段尤其考验耐心与细心,需要对JML进行充分的阅读与理解,让人体会到在大型复杂工程中如何保证各人分工完成的代码正确符合项目要求,以及在工程中对代码描述的严密。