昨天实战营里的G同学提交的代码有这么一段,被我专门圈出来讨论:
🐻:我特别不喜欢continue,很难提取
🐻:所以最好一开始就多想几分钟,能不用就不用
🐻:当然一般来说所有循环都有问题,看见循环就应该多想想
这话不是我说的,是《重构》第二版里面新增的一个坏味道。
我在《不敢止步》里面讲过一个故事。2006年我刚加入ThoughtWorks,就被派到印度参加入职培训。TW的入职培训比较简单,所以我就多带了一本SICP去读。这一读不要紧,打开了一扇全新的大门。尤其是这本书中关于列表和高阶函数的内容会改变一个程序员看待很多编程任务的方式——主要是看待列表(list)操作的方式。
众所周知,LISP是“LISt Processing”的缩写,也就是说“操作链表(linked list)”乃是LISP的题中应有之义。从最原始的递归函数调用开始,SICP归纳出一组基本的列表操作(求长度、添加元素、排序、反转等),然后是几个最常用的列表计算:map、filter、accumulate。这几个概念是如此简洁清晰,实现又是如此简单,一旦进入了脑子里就再也无法忘记。比如map操作,SICP的描述是:
…to apply some transformation to each element in a list and generate the list of results.
SICP
也就是说,你手上有一个列表,你要对这个列表里的每个元素通过“某种方式”进行转换,从而得到一个等长的列表,前后两个列表中的元素根据“某种方式”一一对应。这就是列表的映射(map)操作。于是对于所有“从一个列表映射到另一个等长列表”的操作,你就再也不会去写for循环。例如“列出一组用户的年龄并由大到小排序”这个需求,你就会这样写(Ruby代码):
# users是一个列表users.map(:age).sort.reverse
显然这比每处理一次users列表就写一次for循环要漂亮太多了。有了这样的概念在脑子里,我才开始愈发清晰地意识到:针对列表的for循环,在大多数情况下也是一种重复代码。后来Google的Java基础库Guava提供了列表的transform操作(也就是map操作,只是换了个名字)和filter操作,使这些发轫于LISP的列表操作终于被广大Java程序员所了解。
郑晔前几年写过一个系列文章叫《你应该更新的Java知识》,其中一个重点就是谈Guava。他接受InfoQ采访的时候说:
我对 Guava 的一个评价是,只要你做的是 Java 项目,就应该用 Guava。……Guava 是一个现代的程序库,它有着更易用的 API。当然,Guava 也有一些新增功能,比如,一些集合类、缓存等等。Guava 做得怎么样?看一下 Java 8 的文档就可以知道,有一些 API 几乎就是原封不动地从 Guava 上借鉴来的。 郑晔谈 Java 开发:新工具、新框架、新思维
在列表处理方面,Java 8引入了两个重要的新特性,其一是Stream API,其二是Lambda表达式。没有这两个东西之前,虽然Guava提供了还算不错的列表操作接口,但是实现起来终归比较难看:一个匿名内部类实现,可能并不比一个重复的for循环更优雅。有了Stream和Lambda,用Java就可以写出跟Ruby漂亮程度差不多的代码了。
然而回到现实。我这几天留意了实战营里同学们提交的代码,似乎用Stream接口的人很少,大部分代码都还是对着列表不断在for循环。这是不是一个行业整体水平的缩影,说明行业里大部分程序员的Java基础还停留在至少5年前呢?
今天就不打广告了,改成留思考题吧
G同学被我框出来的那段代码
你觉得应该怎么写才对?