有些语言支持函数指针、代理、lambda表达式,或者类似的机制,允许程序把“调用特殊函数的能力”存储起来并传递这种能力。这种机制通常用于允许函数的调用者通过传入第二个函数,来指定自己的行为。例如C语言库函数中的qsort函数,通过传递不同的比较器函数,就可以获得各种不同的排列顺序,这正是策略模式的一个例子
#include
#include
using namespace std;
int compare(const void *a, const void *b)
{
return *(int*)b-*(int*)a; //从大到小排序
}
int main(){
int a[] = {3,9,8,6,2};
qsort(a, 5, sizeof(int), compare);
for(int i= 0;i<5;i++){
cout<
Java没有提供函数指针,但是可以用对象指针实现同样的功能。我们可以定义这样一种对象,他的方法执行其他对象上的操作,如果一个类仅仅导出这样的一个方法,他的实例实际上就等同于一个指向该方法的指针,这样的实例被称为函数对象,举个栗子:
public class StringLengthComparator {
public int compare(String s1,String s2){
return s1.length()-s2.length();
}
}
为了把StringLengthComparator实例传递给方法,需要适当的参数类型。使用StringLengthComparator并不好,因为客户端将无法传递任何其他的比较策略。相反,通过定义一个Comparator接口,并修改StringLengthComparator来实现这个接口。换句话说,我们在设计具体的策略类时,还需要定义一个策略接口(strategy interface)。
public interface Comparator {
public int compare(T t1,T t2);
}
Comparator接口是泛型的,因此它适合作为除字符串之外的其他对象的比较器。
具体的策略类往往使用匿名类声明,如下代码。
Arrays.sort(new String[]{"1","123"}, new Comparator() {
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
});
注意:以这种方式使用匿名类时,将会在每次执行调用的时候创建一个新的实例。如果它被重复执行,考虑将函数对象存储到一个私有的静态final域里,并重用它。这样做的另一种好处是,可以为这个函数对象取一个有意义的域名称
public class Host {
private static class StrLenCmp implements Comparator {
@Override
public int compare(String s, String t1) {
return s.length()-t1.length();
}
}
public static final Comparator STRATEGY = new StrLenCmp();
}
具体使用
String[] a = new String[]{"111","1","13"};
Arrays.sort(a,Host.STRATEGY);
System.out.print(Arrays.toString(a));//1,13,111
总结:
函数指针的主要用途就是实现策略(Strategy)模式。
为了在Java中实现这种模式,要声明一个接口来表示该策略,并且为每个具体策略声明一个实现了该接口的类。
当一个具体策略只被使用一次时,通常使用匿名类来声明和实例化这个具体策略类。
当一个具体策略是设计用来重复使用的时候,它的类通常就要被实现为私有的静态成员类,并通过公有的静态final域被导出,其类型为该策略接口。