集合管道模式(上)
译者 | TalkingData 李冰心
原文 | https://martinfowler.com/articles/collection-pipeline
译者按:
想了解 Java 流底层的实现机制,发现 Martin Fowler 发表的一篇关于集合管道模式的文章,有幸得以拜读,看过之后收获颇丰,索性将其翻译,能力有限敬请包涵。
集合管道
集合管道是一种编程模式,将相关计算转化为一系列操作,通常情况下每个操作输出结果为一个集合,该输出结果作为下一个操作的输入,常见的有 filter、map 和 reduce。
在函数式编程语言中集合管道模式随处可见,而面向对象语言借助于 lambdas 该模式又大放异彩。如果你对这种模式知之甚少,本文会载着你在各种语言中穿梭,途中会向你讲解相关精彩的案例,相信你一定会不虚此行。
在软件中,集合管道模式操作简单是一种最常见、让人屡试不爽的模式,在 unix 黒白命令行中;在面向对象万物皆对象的类中;在函数式编程一等公民的函数中,不同环境不同表现形式,相同操作却有着不同的名称,知其精髓,你会发现你根本离不开它。
1. Unix 管道
最初接触这种模式,是在 Unix 系统终端进行操作时。
案例:搜索 bliki/entries 目录下,文件内容包含文本'nosql'所有文件。
附加需求 1:分文件统计'nosql'个数
附加需求 2:按个数大小排序
附加需求 3:top4 中的 2~4
可以 unix 管道的简洁和高效。
2. Smalltalk
之后使用 Smalltalk 时也发现了相同的模式。
案例:假设有一系列文章 someArticles,每篇文章 article 有很多标签 tag,想统计包含标签'nosql'的文章。
select 方法定义:使用了 Lambda 表示式方式。
附加需求 1:按照单词数量排序
附加需求 2:top4 中的 2~4
与 unix 管道最核心的相似之处:对于集合操作的 select、sortBy 和 copyFrom 输入输出都是集合,unix 是一行行记录构成的流,而 Smalltalk 是对象集合,但概念相同。
3. Ruby
最近使用 Ruby 进行了很多开发,改进后的语法操作管道更加便捷。
在面向对象语言中,一种很自然构造集合管道的方式是方法链,同样也可以使用嵌套函数。
4. Lisp
为了排序,再次使用了 lambda:
使用方法 subseq:
每种语言逐步构建的集合管道,看起来这是赏心悦目!不过每种语言所传达的含义是否清晰明了有待商议。
对于上述语言方法使用的先后顺序决定了集合管道的操作顺序,在你脑中应该很容易勾画出这种场景:源源不断的数据流入管道,经过层层过滤,最终输出。
5. 嵌套函数
构建管道除了使用流失形式,在 Lisp 中还可以使用嵌套函数,为了便于理解需要控制嵌套的层级。
最近流行的 lisp、Clojure 通过语法糖的形式避免了嵌套层级:
符号 "->>" 是一个线程宏,通过使用 lisp 强大的语法宏功能将每个表达式结果作为下一个表达式的输入。 对于大多数函数开发者,通过使用内置工具库,可游刃有余的将嵌套函数转化为线性管道,导致管道线性划无阻轻重,这也是为什么经过了这么长的时间,lisp 才支持操作符"->>" 。
这些天常听到函粉对集合管道的赞叹,夸奖函数式编程语言的强大,而对面向对象设计语言不屑一顾,这种说法很片面,因为对于面向对象设计语言 Smalltalk,其开发者早已广泛的使用了集合管道,只不过在 C++、Java 和 C#支持的有限而已,例如没有 lambda、集合操作不丰富,以至于很多面向对象开发人员陷入了处理集合管道问题的泥潭中。
Java 大行其道时并不支持 lambdas,对于像我这样 Smalltalk 开发者,使用 Java 时只能一边抱怨一边忍受,曾经尝试过各种方式构建集合管道,但每种实现都需要大量的辅助代码,最终被迫放弃。在 2000 年左右我开始使用 Ruby,重要原因是 Ruby 对于集合管道的支持,回想我的 Smalltalk 时代,常常感慨万千。
Lambdas 短小精悍的语义得到了广泛的赞许,C#已支持多年,现在 Java 也开始支持。集合管道注定会被越来越多的语言支持。
版权声明: 本文为 InfoQ 作者【冰心的小屋】的原创文章。
原文链接:【http://xie.infoq.cn/article/0837e0704734bb0e03c1cdcf3】。文章转载请联系作者。
评论