用函数对象表示策略

2018-06-06  本文已影响0人  没走过的二丁目

有些语言支持函数指针、代理、lambda表达式,或者类似的机制,允许程序把“调用特殊函数的能力”存储起来并传递这种能力。这种机制通常用于允许函数的调用者通过传入第二个函数,来指定自己的行为。例如C语言库函数中的qsort函数,通过传递不同的比较器函数,就可以获得各种不同的排列顺序,这正是策略模式的一个例子

#include<iostream>
#include <stdlib.h>
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<<a[i];//输出98632
    }
}

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<T> {  
    public int compare(T t1,T t2);  
}  

Comparator接口是泛型的,因此它适合作为除字符串之外的其他对象的比较器。
具体的策略类往往使用匿名类声明,如下代码。

Arrays.sort(new String[]{"1","123"}, new Comparator<String>() {  
      public int compare(String o1, String o2) {  
           return o1.length()-o2.length();  
      }  
});

注意:以这种方式使用匿名类时,将会在每次执行调用的时候创建一个新的实例。如果它被重复执行,考虑将函数对象存储到一个私有的静态final域里,并重用它。这样做的另一种好处是,可以为这个函数对象取一个有意义的域名称

public class Host  {
    private static class StrLenCmp implements Comparator<String> {

        @Override
        public int compare(String s, String t1) {
            return s.length()-t1.length();
        }
    }
    public static final Comparator<String> 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域被导出,其类型为该策略接口。

上一篇下一篇

猜你喜欢

热点阅读