《用函数对象表示策略》
这篇文章的题目我又是看了好久才懂。
题目说的其实是,可以把函数当做对象,传给另一个函数作为参数。
有些语言支持函数指针、代理、lambda表达式,或者支持类似的机制,允许程序把“调用特殊函数的能力”储存起来并传递这种能力。这种机制通常用于允许函数的调用者通过传入第二个函数,来指定自己的行为。
这其实是「策略模式」的思想。
和简单工厂模式的区别:
我们去旅行。策略模式的做法:有几种方案供你选择旅行,选择火车好呢还是骑自行车,完全有客户自行决定去构建旅行方案(比如你自己需要去买火车票,或者机票)。而工厂模式是你决定哪种旅行方案后,不用关注这旅行方案怎么给你创建,也就是说你告诉我方案的名称就可以了,然后由工厂代替你去构建具体方案(工厂代替你去买火车票)。
Java没有提供函数指针,但是可以用对象引用实现同样的功能。调用对象上的方法通常是执行该对象上的某个操作。然而,我们也可能定义这样一种对象,它的方法执行其他对象上的操作。如果一个类仅仅导出这样的一个方法,它的实例上就等同于一个指向该方法的指针。这样的实例被称为函数对象。
上周上班的时候写过一个排序的业务,里面有这样一段:
//sort map : value downwards
List<Map.Entry<String, Integer>> sortedMapEntryList =
new ArrayList<>(bizScoreMap.entrySet());
Collections.sort(sortedMapEntryList, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
return o2.getValue() - o1.getValue();
}
});
这个其实就是典型的策略模式了,声明了一个匿名类的对象传给Collections.sort方法当做第二个参数,让sort按照我指定的这种策略排序。Collections
这个类很有意思,里面实现了各种排序,包括binarySearch。我跟进去看了一下,最后用了Arrays.java中的归并函数调用我们传进去的实现Comparator
接口的类的compare
方法:
//Arrays.java
// Merge sorted halves (now in src) into dest
for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
if (q >= high || p < mid && c.compare(src[p], src[q]) <= 0)
dest[i] = src[p++];
else
dest[i] = src[q++];
}
最后,摘抄一段书中的结论:
总而言之,函数指针的主要用途就是实现策略模式。
为了在java中实现这种模式,要声明一个接口来表示该策略,并且为每一个策略声明一个实现了该接口的类。当一个具体策略只被使用一次时,通常使用匿名类来声明和实例化这个具体策略类。
当一个具体策略类时设计用来重复使用的时候,它的类通常就要被实现为私有的静态成员类,并通过公有的静态final域被导出,其类型为该策略接口。