书面定义:JML (Java Modeling Language) 是用于对 Java 程序进行规格化设计的一种表示语言,是一种行为接口规格语言。
个人理解:
设计文档接口,限定输入输出,一个很直观的例子就是本单元大部分指导书都在JML的代码里,不像前两个单元需要做许多的书面定义。
本单元着重训练了面对设计好的JML规格进行开发,其次也训练了依据JML规格设计测试数据。
虽然很熟悉,但几乎没有亲自动手写JML的代码。
有时一行JML可能需要上百行代码来实现(要求高复杂度或辅助方法甚至类)
有时是几行JML也可能只需要一行循环(包含很多\old描述)
并无完善的自动化测试工具,JUnit仅方便模块化测试,对JML无特殊支撑。
根据操作容器的方法特点选择容器,如
@ public instance model non_null Person[] acquaintance;
@ public instance model non_null int[] value;
对应的方法其中有
/*@ public normal_behavior
@ requires (\exists int i; 0 <= i && i < acquaintance.length;
@ acquaintance[i].getId() == person.getId());
@ assignable \nothing;
@ ensures (\exists int i; 0 <= i && i < acquaintance.length;
@ acquaintance[i].getId() == person.getId() && \result == value[i]);
@ also
@ public normal_behavior
@ requires (\forall int i; 0 <= i && i < acquaintance.length;
@ acquaintance[i].getId() != person.getId());
@ ensures \result == 0;
@*/
public /*@pure@*/ int queryValue(Person person);
List
List
的元素一一对应,且通常需要成对操作或根据acquaintance
取value
,因此可以使用Map
合并两个List
根据数据规模选择数据结构,如
/*@ ensures \result == (people.length == 0? 0 : ((\sum int i; 0 <= i && i < people.length;
@ (people[i].getAge() - getAgeMean()) * (people[i].getAge() - getAgeMean())) /
@ people.length));
@*/
public /*@pure@*/ int getAgeVar();
该方法返回组内成员age
的方差,组内成员可以增加或删除
[1,5000]
,age范围[0,200]
,因此可以选择在每次增删人时维护 AgeMean
与AgeVar
( O(1) )(AgeVar
不直接维护,而是维护平方和,用公式计算,注意整除差异!)本单元自己需要设计的方法不多,大部分照着规格都能直接写出,仅梳理几个算法难点。
acquaintance[]
与value[]
合成 HashMap<Person, Integer>
people
用HashMap<Integer, Person>
表示groups
用HashMap<Integer, Group>
messages
用HashMap<Integer, Message>
年龄和
与年龄平方和
O(1),O(1)查询,公式计算方差时注意整除约分带来的影响。valueSum
同样增删人时O(1)维护,O(1)查询(注意2倍),为了维护方便允许破坏类封闭性emojis
用HashMap<Integer, Integer>
dijkstra
,具体实现来说用了priorityQueue
,需要注意从队列中删除节点的时机(如在边界被多次更新最短距离的节点)。还有需要注意priorityQueue
需要实现的比较器,有两个方案
distance
distance
,需要建立辅助类保存距离,有同一节点多个距离的情况,需要及时清理。前两次作业没出现bug,但自己测试时需要注意异常情况下的id(重复id或emojiId)
最后一次作业由于没安排好时间周末下午才开始写,没时间测试了最后强测50,一共两个bug
dijkstra
算法出了问题,priorityQueue
的比较器只存了id,比较实时距离,我当成了比较存入距离进行了清理,强测最短路径的点出了问题。JML虽然没有完善的自动化测试工具,但配合JUnit的单元测试的面向“接口”的测试数据构造更系统,也更全面。
规格理解并不难,难的是选择合适的容器、算法以适应不同的数据规模。
JML有时可以简化描述,但有时不能很好地平衡JML的复杂性,会出现数十行JML语句仅单行循环表示的“反客为主” 的情况,这也是我认为契约式编程的一大缺点,在实际工程中或许可以通过在类似场景“直接描述代码“避免。
现在应该几乎是本学期最忙的时候之一,希望同学们留足测试时间,避免出现时间不够,低级错误导致大量失分的遗憾。
原文:https://www.cnblogs.com/LNTisNotaTree/p/14825490.html