Loading

OO第三单元总结

OO第三单元总结


JML语言的理论基础、应用工具链情况

JML基础语法
  • requires : requires a ,即为前置条件语句,在方法满足条件的情况下实现之后的功能。
  • ensure:ensure a ,即为后置条件语句,在满足ensure对应require前置条件方法执行后对各个容器变量改变的要求。
  • assign:即标明方法执行后的副作用,指明对无关容器变量的影响。
  • public normal_behavior:正常处理部分。
  • public exceptional_behavior:抛出异常部分。
  • \forall:全称量词,具体实现采用遍历容器的for代码实现,对于单层遍历,使用Arraylist遍历较遍历Hashmap复杂度更低。
  • \exists:存在量词,具体为实现选取容器中的特定元素,使用key与value一一映射的Hashmap即可。
  • \old:old a ,表示方法使用改变a之前的值,常见于表示增加删除容器元素后容器的容量变化。
工具链应用
  • Junit:单元检测
  • OpenJml:JML语法检测
  • JMLUnitNG:生成测试样例,主要为爆破边界。

部署SMT Solver

折腾了半天jdk,魔改demo,还是会有各种奇怪的报错,遂quit。根据部署成功的同学的介绍,由于SMT在遇到\forall, \exists等复杂逻辑语句时会报错,对我们大学的OO单元作业用处并不太大。

部署JMLUnitNG/JMLUnit

JMLUnitNG

总算配成功了orz
根据J哥的博客,部署好文件树后,输入一下代码来生成对MyGroup的测试,

java -jar jmlunitng.jar jmlunit/MyGroup.java
javac -cp jmlunitng.jar jmlunit/*.java
java -jar openjml.jar -rac jmlunit/MyGroup.java
java -cp jmlunitng.jar jmlunit.MyGroup_JML_Test

最后得到如下反馈

[TestNG] Running:
  Command line suite

Passed: racEnabled()
Passed: constructor MyGroup(-2147483648)
Passed: constructor MyGroup(0)
Passed: constructor MyGroup(2147483647)
Failed: <<MyGroup@4ebba42a>>.addPerson(null)
Failed: <<MyGroup@467eca23>>.addPerson(null)
Failed: <<MyGroup@66133adc>>.delPerson(null)
Failed: <<MyGroup@287534f5>>.delPerson(null)
Failed: <<MyGroup@ba4d4a7e>>.delPerson(null)
Passed:<<MyGroup@5c0e39b3>>.equals(null)
Passed:<<MyGroup@d793cb21>>.equals(null)
Passed:<<MyGroup@508a4bba>>.equals(null)
Passed: <<MyGroup@a2b1398c>>.equals(java.lang.Object@574caa3f)
Passed: <<MyGroup@63bcaa71>>.equals(java.lang.Object@64cee07)
Passed: <<MyGroup@2d7f89e3>>.equals(java.lang.Object@1761e840)
Passed: <<MyGroup@23fea612>>.getAgeMean()
Passed: <<MyGroup@b45e2fa7>>.getAgeMean()
Passed: <<MyGroup@7a645219>>.getAgeMean()
Passed: <<MyGroup@685b3ad2>>.getAgeVar()
Passed: <<MyGroup@3536c874>>.getAgeVar()
Passed: <<MyGroup@c2f309a6>>.getAgeVar()
Passed: <<MyGroup@ab27c89c>>.getConflictSum()
Passed: <<MyGroup@1ef7015a>>.getConflictSum()
Passed: <<MyGroup@74b8b58f>>.getConflictSum()
Passed: <<MyGroup@1c56bc89>>.getId()
Passed: <<MyGroup@445a8ca1>>.getId()
Passed: <<MyGroup@38b6c32a>>.getId()
Passed: <<MyGroup@3ae8b12a>>.getRelationSum()
Passed: <<MyGroup@3e2851ac>>.getRelationSum()
Passed: <<MyGroup@a93c83c7>>.getValueSum()
Passed: <<MyGroup@721f0239>>.getValueSum()
Passed: <<MyGroup@6ab01c56>>.getValueSum()
Failed: <<MyGroup@1f6ff78c>>.hasPerson(null)
Failed: <<MyGroup@c81f7a90>>.hasPerson(null)
Failed: <<MyGroup@48e75ac0>>.hasPerson(null)
Passed: <<MyGroup@5e876f2a>>.updatePerson(null)
Passed: <<MyGroup@6f91e371>>.updatePerson(null)
Passed: <<MyGroup@b274b4d7>>.updatePerson(null)
Passed: <<MyGroup@13fa6a7b>>.downloadPerson(null)
Passed: <<MyGroup@49a3b226>>.downloadPerson(null)
Passed: <<MyGroup@6821a38b>>.downloadPerson(null)
Failed: <<MyGroup@21ab83e5>>.updateLink(null, null)
Failed: <<MyGroup@48e2ff12>>.updateLink(null, null)
Failed: <<MyGroup@67a8815e>>.updateLink(null, null)

===============================================
Command line suite
Total tests run: 44, Failures: 11, Skips: 0
===============================================

可以发现JUMLUnitNG生成的测试样例大部分为null,INT_MAX,0,等等极其边界条件,针对本单元作业没有太大作用。

架构设计与模型构建策略

第九次作业

由于是新单元的第一次作业,整体结构直接根据官方的接口,建立MyPerson,Mynetwork三个大类即可,接口的实现绝大部分可以照搬JML翻译,其中需要考量的有两点:iscircle的算法选择,与不同属性的容器选择。

  • isclrcle:采用BFS:由于DFS存在爆栈的可能,担心被某些饥渴的狼人🔪中,遂放弃。
  • 容器选择:用于Hashmap有红黑树等的隐形优化,除去要求且仅要求每次遍历的数据使用Arraylist外,其余的使用Hashmap存储。
第十次作业

第二次作业相较于第一次作业主要新加Group大类,并根据Goup的成员实现group中所有成员年龄平均值,方差等方法,值得思考的同第九次作业有两点:Group容器的选择;qgrs,qgvs如何优化二重遍历,避免O(n^2)。

  • Group容器的构造:由于Group中的方法对遍历group全体成员与选取单个成员都有较多的要求,因此我选择分别建立private HashMap<Integer, Person> people1 = new HashMap<>(8192)private ArrayList<Person> people2 = new ArrayList<>(8192)两个容器来存取group中的person信息,在选取判断特定成员时,使用Hashamap,遍历全体时使用Arraylist,能有效缩短由于Hashmap预留的空间而导致遍历全部的key-value时间开销过大。并根据Group存储person的最大数量限制,为两个容器开设8192的初始容量,避免后续添加person时单独开设空间的时间浪费。
  • 二重遍历的优化:采用及时建立缓冲的策略,在addRelation与addtoGroup的过程中及时更新Group中的relationSum与valueSum。
第十一次作业

第三次作业的难点集中在三个方法的实现qmp, qsl, qbs,主要的设计与时间投入也集中在这块。

  • qmp:采用Dijsktra + Priorityqueue优化的算法。
  • qsl:根据JML的ensure分析,先通过DFS遍历得到所有的路径,if:不存在节点个数大于等于3的路径,return false;else:选取该路径,遍历该路径上除去首尾的所有点,遮掩该点后再次DFS,判断有无第二条路径。
  • qbs:采用并查集 + 路径压缩的算法,主要代码如下,分别进行的功能。
	public Person findPa(Person son) {
        if (!pre.get(son).equals(son)) {
            pre.replace(son, findPa(pre.get(son)));
        }
        return pre.get(son);
    }

	void merge(Person son1,Person son2) {
        Person papa1 = findPa(son1);
        Person papa2 = findPa(son2);
        if (!papa1.equals(papa2)) {
            pre.replace(papa2, papa1);
        }
    }

bug和修复情况

BUG分析
  • 第一次由于首次接触JML,书写时照搬JML语句people.get(i) == person,而没使用重写的equal方法。
  • 第三次中用手搓DIJ + PriorityQueue荣幸获得边界tle,与同学代码比对后发现具体实现没有太大差异,最后发现是Person.acq使用hashmap时初始化大小过大,导致qmp遍历时耗时过多,导致tle。emmm,出这种错蛮无语的,当然也说明测试时极端数据没有构造的太好。
HACK策略
  • 第一次和第二次都没🔪中。
  • 第三次主要针对qmp构造极限数目的点连乘一条直线,针对qsl的多重深搜构造多环图,Hack刀了3个小伙伴。

阐述对规格撰写和理解上的心得体会

第三单元感想体会

自我感觉第三单元的整体难度较第二单元有所下降,主要重点集中在对JML的理解,即该方法实现的目的是什么来思考,而不是机械的翻译。第三次作业由于涉及性能要求,考察了一定算法知识,思考算法层面的时间占据了绝大部分。另外一点便是由于JML已经给出了大部分的代码框架,且群佬除我,因此有一点微小的错误都会造成强测直接被专业团队愉快送走,对我们自动化测试与极端测试数据的构造都有了极大要求,all in all ,对拍真香。

posted @ 2020-05-20 22:55  Ethanscript  阅读(151)  评论(0编辑  收藏  举报