第21条:用函数对象表示策略

有些语言支持函数指针(function pointer)、代理(delegate)、lambda表达式(lambda expression),或者支持类似的机制,允许程序把“调用特殊函数的能力”存储起来并传递这种能力。这种机制通常用于允许函数的调用者通过传入第二个函数,来指定自己的行为。

Java中没有提供函数指针,但是可以用对象引用实现同样的功能。调用对象上的方法通常是执行该对象(that object)上的某项操作。然而,我们也可能定义这样一种对象,它的方法执行其它对象(other object)(这些对象被显示传递给这些方法)上的操作。如果一个类仅仅导出这样的一个方法,它的实例实际上就等同于一个指向该方法的指针。这样的实例被称为函数对象(function object)。例如考虑下面的类:

1 public class StringLengthComparator {
2     public int compare(String s1,String s2) {
3         return s1.length() - s2.length();
4     }
5 }

这个类导出一个带两个字符串参数的方法,这个方法是一个比较器,它根据长度来给字符串排序,而不是根据更常用的字典顺序。指向StringLengthComparator对象的引用可以被当做是一个指向该比较器的“函数指针(function pointer)”,可以在任意一对字符串上被调用。换句话说,StringLengthComparator实例就是用于字符串比较操作的具体策略(concrete strategy)。

作为典型的具体策略类,StringLengthComparator类是无状态的(stateless):它没有域,所以这个类的所有实例在功能上都是相互等价的。因此,它作为一个Singleton是非常合适的,可以节省不必要的对象创建开销:

1 public class StringLengthComparator {
2     private StringLengthComparator() {};
3     public static final StringLengthComparator INSTANCE =
4             new StringLengthComparator();
5     public int compare(String s1,String s2) {
6         return s1.length() - s2.length();
7     }
8 }

为了把StringLengthComparator实例传递给方法,需要适当的参数类型。使用StringLengthComparator并不好,因为客户端将无法传递任何其他的比较策略。相反,我们要定义一个Comparator接口,并修改StringLengthComparator来实现这个接口。换句话说,我们在设计具体的策略类时,还需要定义一个策略接口(strategy interface),如下所示:

1 public interface Comparator<T> {
2     public int compare(T t1,T t2);
3 }

Comparator接口的这个定义也出现在java.util包中。Comparator接口是泛型的,因此它适合作为除字符串之外的其它对象的比较器,它的compare方法的两个参数类型为T(它正常的类型参数),而不是String。只要声明前面所示的StringLengthComparator类要这么做,就可以用它实现Comparator<String>接口:

1 public class StringLengthComparator implements Comparator<String>{
2     private StringLengthComparator() {};
3     public static final StringLengthComparator INSTANCE =
4             new StringLengthComparator();
5     public int compare(String s1,String s2) {
6         return s1.length() - s2.length();
7     }
8 }

具体的策略类往往使用匿名类声明,下面的语句根据长度对一个字符串数组进行排序:

 1 String[] stringArray = new String[] {"remotes","result"};
 2 Arrays.sort(stringArray, new Comparator<String>() {
 3     @Override
 4     public int compare(String s1, String s2) {
 5         return s1.length() - s2.length();
 6     }
 7 });
 8 for (String string : stringArray) {
 9     System.out.println(string);
10 }

运行结果:

result
remotes

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

因为策略接口被用作所有具体策略实例的类型,所以我们并不需要为了导出具体策略,而把具体策略做成公有的。相反,“宿主类(host class)”还可以导出公有的静态域(或者静态工厂方法),其类型为策略接口,具体的策略接口类可以是宿主类的私有嵌套类。下面的例子使用静态成员类,而不是匿名类,以便允许具体的策略类实现第二个接口Serializable:

 1 class Host{
 2     private static class StrLenCmp implements Comparator<String>,Serializable{
 3         @Override
 4         public int compare(String t1, String t2) {
 5             return t1.length() - t2.length();
 6         }
 7     }
 8     public static final Comparator<String> STRING_LENGTH_COMPARATOR =
 9             new StrLenCmp();
10 }

String类利用这种模式,通过它的CASE_INSENSITIVE_ORDER域,导出一个不区分大小写的字符串比较器。

简而言之,函数指针的主要用途就是实现策略(strategy)模式。为了在Java中实现这种模式,要声明一个接口来表示该策略,并且为每个具体策略声明一个实现了该接口的类。当一个具体策略只被使用一次时,通常使用匿名类来声明和实例化这个具体策略模类。当一个具体策略是设计用来重复使用的时候,它的类通常就要被实现为私有的静态成员类,并通过公有的静态final域被导出,其类型为该策略接口。

转载于:https://www.cnblogs.com/remote/p/10121602.html

第21条:用函数对象表示策略相关推荐

  1. 52条SQL语句性能优化策略

    本文会提到 52 条 SQL 语句性能优化策略. 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 WHERE 及 ORDER BY 涉及的列上建立索引. 2.应尽量避免在 WHERE 子句中对字 ...

  2. sql加上唯一索引后批量插入_阿里大佬总结的52条SQL语句性能优化策略,建议收藏...

    你知道的越多,不知道的就越多,业余的像一棵小草! 你来,我们一起精进!你不来,我和你的竞争对手一起精进! 编辑:业余草 cnblogs.com/SimpleWu/p/9929043.html 推荐:h ...

  3. CakePHP你必须知道的21条技巧

    原文链接:http://www.avatarfinancial.com/pages/cake/ 这篇文章可以说是CakePHP教程中最经典的了.虽然不是完整的手把手系列, 但作者将自己使用CakePH ...

  4. Redis使用的21条军规(规范)

    Redis使用的21条军规(规范) 文章目录 Redis使用的21条军规(规范) 前言 一.基础规范[5条] 二.键值设计[4条] 三.操作命令[4条] 四.内存优化[4条] 五.集群架构[4条] 仰 ...

  5. 最新一线大厂Redis使用21条军规及详细解读

    说明:个人原创,本人在一线互联网大厂维护着几千套集群,关于redis使用的一些坑进行了经验总结,希望能给大家带来一些帮助 适用场景:并发量大.访问量大的业务 规范:介绍军规内容 解读:讲解军规设置原因 ...

  6. 【MySQL】47 条SQL语句性能优化策略

    本文会提到 47 条 SQL 语句性能优化策略. 1. 对查询进行优化,应尽量避免全表扫描,首先应考虑在 WHERE 及 ORDER BY 涉及的列上建立索引. 2. 应尽量避免在 WHERE 子句中 ...

  7. 52条SQL语句性能优化策略,建议收藏

    点击上方 "编程技术圈"关注, 星标或置顶一起成长 后台回复"大礼包"有惊喜礼包! 每日英文 Promise yourself to be so strong ...

  8. 52条 SQL 语句性能优化策略全面解析,你知道多少种?建议收藏!

    点击上方 "编程技术圈"关注, 星标或置顶一起成长 后台回复"大礼包"有惊喜礼包! 每日英文 Don't always in the memories of t ...

  9. 函数对象、 函数对象与容器、函数对象与算法

    一.函数对象 1.函数对象(function object)也称为仿函数(functor) 2.一个行为类似函数的对象,它可以没有参数,也可以带有若干参数. 3.任何重载了调用运算符operator( ...

最新文章

  1. struts2学习笔记--使用servletAPI实现ajax的一个小Demo
  2. 文本分类入门(四)训练Part 1
  3. 用802.11n 加速,将android手机屏幕投影到win7电脑上
  4. Django上传文件,制作文件上传按钮,form上传文件
  5. 页面中鼠标触碰字体后切换颜色且随鼠标移走后改变
  6. Spring-aop-AbstractAutoProxyCreator
  7. 一道海量日志的随机选取问题
  8. python中numpy matplotlib绘图教程_利用numpy+matplotlib绘图的基本操作教程
  9. 绘图神器 —— Graphviz 绘制数据结构相关图形
  10. swift学习笔记《5》- 实用
  11. logging日志模块 , 序列化json pickle , 随机数random
  12. 控制理论中的几种稳定性介绍
  13. 移动端WEB开发过程中小米浏览器的一个坑?
  14. Oracle 创建表详解(create table)
  15. 如何彻底删除adobe?adobe官方清理工具怎么用?
  16. 韩昊20190912-3 词频统计
  17. 蓝桥杯应该参加吗?如何准备蓝桥杯?
  18. centos7/8配置secondary ip
  19. flask入门和进阶五(关注功能的实现Followers)
  20. Unity Fleck Map 参数说明

热门文章

  1. Python3——JSON
  2. 每日一题(53)—— 评价代码片段
  3. python 串口_如何使用Python开发串口通讯上位机(一)
  4. Struts2_1_基础案例_配置文件详解_动作类
  5. AttributeError: ‘set‘ object has no attribute ‘items‘
  6. python工控怎么样_搞工控不了解python,好比雄鹰断了翅膀,理由在这里!
  7. Uniapp学习笔记(数据展示、数据循环、条件编译、计算属性、组件的使用、组件插槽、生命周期)
  8. LeetCode 2129. 将标题首字母大写
  9. LeetCode 1834. 单线程 CPU(排序 + 优先队列)
  10. LeetCode 1428. 至少有一个 1 的最左端列(二分查找)