使用循环和收集管道进行重构:第 1 部分

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 54w+ 字,讲解图 2476+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 1900+ 小伙伴加入学习 ,欢迎点击围观

循环是处理集合的经典方式,但随着编程语言中一流函数的更多采用,集合管道成为一种有吸引力的替代方法。在这篇文章中,我通过一系列小示例研究了将循环重构为收集管道。

我分期发表这篇文章。这添加了一个重构循环的示例,该循环汇总了每个目的地机场的航班延误数据。

编程中的一个常见任务是处理对象列表。大多数程序员自然地使用循环来执行此操作,因为它是我们在第一个程序中学习的基本控制结构之一。但是循环并不是表示列表处理的唯一方式,近年来越来越多的人使用另一种方式,我称之为 集合管道 。这种风格通常被认为是函数式编程的一部分,但我在 Smalltalk 中大量使用它。由于 OO 语言支持使一流函数更易于编程的 lambda 和库,因此集合管道成为一个有吸引力的选择。


将简单循环重构为管道

我将从一个简单的循环示例开始,展示我将循环重构为收集管道的基本方法。

假设我们有一个作者列表,每个作者都有以下数据结构。

班级作者...


 public string Name { get; set; }
  public string TwitterHandle { get; set;}
  public string Company { get; set;}

这个例子使用 C#

这是循环。

班级作者...


 public string Name { get; set; }
  public string TwitterHandle { get; set;}
  public string Company { get; set;}

将循环重构为集合管道的第一步是在循环集合上应用 提取变量

班级作者...


 public string Name { get; set; }
  public string TwitterHandle { get; set;}
  public string Company { get; set;}

这个变量为我提供了管道操作的起点。我现在还没有一个好名字,所以我会使用一个暂时有意义的名字,希望以后可以重命名。

然后我开始查看循环中的一些行为。我首先看到的是条件检查,我可以使用 .

班级作者...


 public string Name { get; set; }
  public string TwitterHandle { get; set;}
  public string Company { get; set;}

我看到循环的下一部分在 twitter 句柄上运行,而不是作者,所以我可以使用 aa 。

班级作者...


 public string Name { get; set; }
  public string TwitterHandle { get; set;}
  public string Company { get; set;}

Next 在循环中作为另一个条件,我可以再次移动到过滤操作。

班级作者...


 public string Name { get; set; }
  public string TwitterHandle { get; set;}
  public string Company { get; set;}

循环现在所做的就是将其循环集合中的所有内容添加到结果集合中,这样我就可以删除循环并只返回管道结果。

班级作者...


 public string Name { get; set; }
  public string TwitterHandle { get; set;}
  public string Company { get; set;}

这是代码的最终状态

班级作者...


 public string Name { get; set; }
  public string TwitterHandle { get; set;}
  public string Company { get; set;}

我喜欢集合管道的一点是,当列表的元素通过管道时,我可以看到逻辑流。对我来说,它非常接近于我如何定义循环的结果“选择作者,选择拥有公司的人,并让他们的推特句柄删除任何空句柄”。

此外,即使在具有不同语法和管道运算符不同名称的不同语言中,这种代码风格也很熟悉。

爪哇


 public string Name { get; set; }
  public string TwitterHandle { get; set;}
  public string Company { get; set;}

红宝石


 public string Name { get; set; }
  public string TwitterHandle { get; set;}
  public string Company { get; set;}

虽然这与其他示例相匹配,但我会用 compact 替换最终的 reject

Clojure 语言


 public string Name { get; set; }
  public string TwitterHandle { get; set;}
  public string Company { get; set;}

F#


 public string Name { get; set; }
  public string TwitterHandle { get; set;}
  public string Company { get; set;}

同样,如果我不关心匹配其他示例的结构,我会合并地图并选择一个步骤

我发现,一旦我习惯了从管道的角度思考问题,我就可以快速应用它们,即使是使用一种不熟悉的语言。由于基本方法是相同的,因此即使是不熟悉的语法和函数名称也相对容易翻译。

在流水线中重构,并理解

一旦您将某些行为表示为管道,您就可以通过对管道中的步骤重新排序来进行潜在的重构。一个这样的举动是,如果你有一个地图后跟一个过滤器,你通常可以像这样将过滤器移动到地图之前。

班级作者...


 public string Name { get; set; }
  public string TwitterHandle { get; set;}
  public string Company { get; set;}

当您有两个相邻的过滤器时,您可以使用连词将它们组合起来。

班级作者...


 public string Name { get; set; }
  public string TwitterHandle { get; set;}
  public string Company { get; set;}

一旦我有了一个像这样的简单过滤器和映射形式的 C# 收集管道,我就可以用 Linq 表达式替换它

班级作者...


 public string Name { get; set; }
  public string TwitterHandle { get; set;}
  public string Company { get; set;}

我认为 Linq 表达式是 的一种形式,类似地,您可以使用任何支持列表理解的语言来执行类似的操作。您更喜欢列表理解形式还是管道形式(我更喜欢管道),这是一个品味问题。一般来说,管道更强大,因为你不能将所有管道重构为理解。