第21条:用函数对象表示策略
第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条:用函数对象表示策略相关推荐
- 52条SQL语句性能优化策略
本文会提到 52 条 SQL 语句性能优化策略. 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 WHERE 及 ORDER BY 涉及的列上建立索引. 2.应尽量避免在 WHERE 子句中对字 ...
- sql加上唯一索引后批量插入_阿里大佬总结的52条SQL语句性能优化策略,建议收藏...
你知道的越多,不知道的就越多,业余的像一棵小草! 你来,我们一起精进!你不来,我和你的竞争对手一起精进! 编辑:业余草 cnblogs.com/SimpleWu/p/9929043.html 推荐:h ...
- CakePHP你必须知道的21条技巧
原文链接:http://www.avatarfinancial.com/pages/cake/ 这篇文章可以说是CakePHP教程中最经典的了.虽然不是完整的手把手系列, 但作者将自己使用CakePH ...
- Redis使用的21条军规(规范)
Redis使用的21条军规(规范) 文章目录 Redis使用的21条军规(规范) 前言 一.基础规范[5条] 二.键值设计[4条] 三.操作命令[4条] 四.内存优化[4条] 五.集群架构[4条] 仰 ...
- 最新一线大厂Redis使用21条军规及详细解读
说明:个人原创,本人在一线互联网大厂维护着几千套集群,关于redis使用的一些坑进行了经验总结,希望能给大家带来一些帮助 适用场景:并发量大.访问量大的业务 规范:介绍军规内容 解读:讲解军规设置原因 ...
- 【MySQL】47 条SQL语句性能优化策略
本文会提到 47 条 SQL 语句性能优化策略. 1. 对查询进行优化,应尽量避免全表扫描,首先应考虑在 WHERE 及 ORDER BY 涉及的列上建立索引. 2. 应尽量避免在 WHERE 子句中 ...
- 52条SQL语句性能优化策略,建议收藏
点击上方 "编程技术圈"关注, 星标或置顶一起成长 后台回复"大礼包"有惊喜礼包! 每日英文 Promise yourself to be so strong ...
- 52条 SQL 语句性能优化策略全面解析,你知道多少种?建议收藏!
点击上方 "编程技术圈"关注, 星标或置顶一起成长 后台回复"大礼包"有惊喜礼包! 每日英文 Don't always in the memories of t ...
- 函数对象、 函数对象与容器、函数对象与算法
一.函数对象 1.函数对象(function object)也称为仿函数(functor) 2.一个行为类似函数的对象,它可以没有参数,也可以带有若干参数. 3.任何重载了调用运算符operator( ...
最新文章
- struts2学习笔记--使用servletAPI实现ajax的一个小Demo
- 文本分类入门(四)训练Part 1
- 用802.11n 加速,将android手机屏幕投影到win7电脑上
- Django上传文件,制作文件上传按钮,form上传文件
- 页面中鼠标触碰字体后切换颜色且随鼠标移走后改变
- Spring-aop-AbstractAutoProxyCreator
- 一道海量日志的随机选取问题
- python中numpy matplotlib绘图教程_利用numpy+matplotlib绘图的基本操作教程
- 绘图神器 —— Graphviz 绘制数据结构相关图形
- swift学习笔记《5》- 实用
- logging日志模块 , 序列化json pickle , 随机数random
- 控制理论中的几种稳定性介绍
- 移动端WEB开发过程中小米浏览器的一个坑?
- Oracle 创建表详解(create table)
- 如何彻底删除adobe?adobe官方清理工具怎么用?
- 韩昊20190912-3 词频统计
- 蓝桥杯应该参加吗?如何准备蓝桥杯?
- centos7/8配置secondary ip
- flask入门和进阶五(关注功能的实现Followers)
- Unity Fleck Map 参数说明
热门文章
- Python3——JSON
- 每日一题(53)—— 评价代码片段
- python 串口_如何使用Python开发串口通讯上位机(一)
- Struts2_1_基础案例_配置文件详解_动作类
- AttributeError: ‘set‘ object has no attribute ‘items‘
- python工控怎么样_搞工控不了解python,好比雄鹰断了翅膀,理由在这里!
- Uniapp学习笔记(数据展示、数据循环、条件编译、计算属性、组件的使用、组件插槽、生命周期)
- LeetCode 2129. 将标题首字母大写
- LeetCode 1834. 单线程 CPU(排序 + 优先队列)
- LeetCode 1428. 至少有一个 1 的最左端列(二分查找)