行为等价性
在某个所给的规约情况下,两个程序的表现在用户看来是完全相同的,称为行为等价性。
规约
作用:帮助程序员协作、明确目标,作为用户的使用说明。同时程序的具体实现用户不需要知道,起到了保护的作用
原则:“你若违反规则,我便不负责任”
构成:方法声明+先决条件+结果说明
static int find(int[] arr, int val)
requires: val occurs exactly once in arr
effects: returns index i such that arr[i] = val
Java规约
由于java常用的文档格式,下列规约被认为是java规约的标准格式
/**
* Find a value in an array.
* @param arr array to search, requires that val occurs exactly once
* in arr
* @param val value to search for
* @return index i such that arr[i] = val
*/
static int find(int[] arr, int val)
1、尽量将先决条件写入@param中,结果说明写入@return中
2、注释中不应出现变量类型(已经在方法声明中显示出来了)
3、绝不能涉及到实现细节
在IDEA中,可以看到spec和一般注释的颜色区别,注意第一行*的个数区别
/*
* 一般注释是黑色的
*/
/**
* 规约是绿色的
*/
同时应注意说明方法是否改变了对象本身,下图为改变和不改变的对比:
// a mutating method:
static void sort(List<String> list)
requires: nothing
effects: puts list in sorted order, i.e. list[i] ≤ list[j] for all 0 ≤ i < j < list.size()
// not mutating:
static List<String> toLowerCase(List<String> list)
requires: nothing
effects: returns a new list t where t[i] = list[i].toLowerCase()
杜绝null的使用
String[] names = new String[] { null };
List<Double> sizes = new ArrayList<>();
sizes.add(null);
上述代码的snapshot如下图,可以看到用户如果不注意null和其他成员是有区别的,使用某些方法时会报错
虽然可以显示地向用户指出,但是更好的方法是杜绝null,可以如下声明
static boolean addAll(@NonNull List<T> list1, @NonNull List<T> list2)
java中原始变量不能赋值为null,而原始变量的wrapper和引用变量可以为空
测试
一般分为白盒测试与黑盒测试。
黑盒测试按照给定的规约测试程序的表现。需要注意的是,白盒测试也需要按照规约进行,应给出在规约内的测试用例。
考虑如下spec:
static int find(int[] arr, int val)
requires: val occurs in arr
effects: returns index i such that arr[i] = val
可以看到结果说明并没有确定多个val时返回哪个位置,所以测试时也不应强行指定,超过结果说明的要求。
int[] array = new int[] { 7, 7, 7 };
int i = find(array, 7);
assertEquals(0, i); // bad test case: assumes too much, more than the postcondition promises
assertEquals(7, array[i]); // correct