规约的评判维度
确定性(deterministic):对于确定的输入,规约规定的输出的确定性
声明性(declarative):不说明具体实现步骤,只是单纯地说明程序表现
功能性(strong):能用更宽松的先决条件实现更满意的结果
确定性(deterministic)VS不确定性(undeterministic)
考虑下列两个spec的区别
static int findExactlyOne(int[] arr, int val)
requires: val occurs exactly once in arr
effects: returns index i such that arr[i] = val
static int findOneOrMore,AnyIndex(int[] arr, int val)
requires: val occurs in arr
effects: returns index i such that arr[i] = val
可以看到上面的规约确定性比下面的强,且上面的规约具有确定性,下面的具有不确定性。我们推崇更确定的规约。
声明性(declarative)VS操作性(operational)
声明性:给出此程序的先决条件与结果说明,但不涉及具体实现
操作性:给出此程序实现的步骤与细节
下图规约是操作性的,因为它说明了方法是如何实现的:
static String join(String delimiter, String[] elements)
effects: append together the strings in elements, but at each step,
if there are more elements left, insert delimiter
可以将上述spec改为声明性的:
effects: returns concatenation of elements in order, with delimiter
inserted between each pair of adjacent elements
我们推崇声明性的规约。
功能强(Strong)VS功能弱(Weak)
当SP1的先决条件相等或弱于SP2,而SP1的结果说明强于或相等于SP2,则SP1强于SP2,同时SP1的实现方法会没有SP2多。
可以看到下列规约功能性强度从上到下递增:
// 原版
static int findExactlyOne(int[] a, int val)
requires: val occurs exactly once in a
effects: returns index i such that a[i] = val
// 先决条件变弱,结果说明不变
static int findOneOrMore,AnyIndex(int[] a, int val)
requires: val occurs at least once in a
effects: returns index i such that a[i] = val
// 先决条件不变,结果说明变强
static int findOneOrMore,FirstIndex(int[] a, int val)
requires: val occurs at least once in a
effects: returns lowest index i such that a[i] = va
可以用图形化的规约描述,其中每个点代表规约的一种实现方式,规约面积越大,实现方式越多。故功能性越强的规约图形面积越小。
一些忠告
- 尽量独立功能:
static int sumFind(int[] a, int[] b, int val)
effects: returns the sum of all indices in arrays a and b at which
val appears
上述规约实现了查找和求和两个功能,将其分开为两个方法会提高可读性。
- 能区分不同情况:
static V put(Map<K,V> map, K key, V val)
requires: val may be null, and map may contain null values
effects: inserts (key, val) into the mapping, overriding any existing
mapping for key, and returns old value for key, unless none,
in which case it returns null
上述规约方法返回null时并不能区分元素本身就是null还是并没有找到元素。
- 功能性尽量强
尽量减少用户的烦恼,尽量提高方法的价值
- 在某些时候,功能性应尽量弱
static File open(String filename)
effects: opens a file named filename
上述规约太强,程序员将耗费大量精力。因为读文件有很多意外情况,程序员需要一一考虑,此时不如降低功能性。
- 应抽象一点
static ArrayList<T> reverse(ArrayList<T> list)
effects: returns a new list which is the reversal of list,
i.e. newList[i] = list[n-i-1] for all 0 ≤ i < n, where n = list.size()
上述规约限定类型为ArrayList,改为List会更好。