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

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

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

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

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

编程中的一个常见任务是处理对象列表。大多数程序员自然地使用循环来执行此操作,因为它是我们在第一个程序中学习的基本控制结构之一。但是循环并不是表示列表处理的唯一方式,近年来越来越多的人使用另一种方式,我称之为 集合管道 。这种风格通常被认为是函数式编程的一部分,但我在 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 表达式是 的一种形式,类似地,您可以使用任何支持列表理解的语言来执行类似的操作。您更喜欢列表理解形式还是管道形式(我更喜欢管道),这是一个品味问题。一般来说,管道更强大,因为你不能将所有管道重构为理解。