Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

02 _过滤器模式 Filter Pattern

1. 基本信息

中文名称: 过滤器模式

英文名称: Filter Pattern

模式类型: 行为型设计模式

Rust 中常见实现方式: 迭代器链、filter()、闭包、map()fold()collect()

过滤器模式的核心是:对集合中的元素按照某些条件进行筛选或过滤,并可以组合多个条件形成处理管道。 在 Rust 中,过滤器模式通常依赖迭代器链和闭包表达筛选逻辑,从而形成声明式的数据处理流程。

示例代码


2. 模式核心思想

过滤器模式的核心思想是:

将对元素的条件筛选逻辑抽象出来,使数据处理逻辑与具体集合类型解耦,并允许多个筛选条件组合成管道式处理。

也可以理解为:它是迭代器模式在“条件筛选”上的具体应用。


3. 这个模式解决什么问题

在实际开发中,我们经常需要对数据集合进行筛选、过滤或分组操作,例如:

1. 过滤掉不符合条件的用户
2. 从日志中筛选错误记录
3. 提取数据表中满足条件的行
4. 网络数据流中选择特定类型的数据

如果每次筛选都写显式循环和条件判断:

  • 代码重复
  • 条件逻辑分散
  • 集合访问和过滤混合
  • 后续扩展或组合多个条件困难

过滤器模式通过将筛选条件抽象成闭包或函数,并结合迭代器,可以统一地对集合元素进行处理,同时支持条件组合和链式操作。


4. 不使用这个模式会怎样

如果不使用过滤器模式,代码中可能出现:

1. 对不同集合重复写循环和条件判断
2. 条件逻辑散落在各处,难以维护
3. 集合处理逻辑和业务逻辑耦合
4. 扩展新的过滤条件时需要改动现有循环
5. 组合多个条件操作时代码容易臃肿

在 Rust 中,如果完全手写 for 循环 + if 条件判断,会出现很多临时变量、嵌套判断和重复逻辑,降低代码可读性。


5. 传统面向对象中的实现思路

在传统 OOP 语言中,过滤器模式通常会将每个过滤条件封装为一个接口或类。例如:

Filter 接口
    └── matches(item) -> bool

具体过滤器类
    ├── NameFilter
    ├── AgeFilter
    ├── StatusFilter

然后通过组合多个过滤器对象来实现复杂筛选逻辑,例如:

AndFilter
OrFilter

这样调用方可以不关心集合内部结构,只依赖统一接口调用 matches 方法。


6. Rust 中的实现思路

6.1 Rust 中通常怎么实现

在 Rust 中,过滤器模式通常建立在迭代器模式之上,利用 filter() 方法和闭包实现。

示意流程:

集合.iter()
       .filter(|item| 条件1)
       .filter(|item| 条件2)
       .map(|item| 转换)
       .collect::<Vec<_>>();

闭包负责表达条件逻辑,迭代器负责遍历集合。

6.2 和传统 OOP 写法相比有什么不同

  1. 不需要为每个条件创建单独对象或类。条件通过闭包表达即可。
  2. 组合条件非常轻量,通过连续调用 filter() 或自定义闭包组合即可。
  3. 遍历和过滤通常是惰性求值,只有消费操作如 collect() 才触发计算。
  4. 不依赖接口继承,也不需要手动管理迭代器对象。

6.3 Rust 中是否有更自然的替代写法

在 Rust 中,如果数据量小、条件简单,也可以直接在 for 循环中加 if 语句实现过滤逻辑。但在大多数集合处理、链式处理或管道处理场景下,迭代器 + filter 更自然、更可组合。


7. Rust 中涉及的语言特性

1. Iterator trait
2. filter() 方法
3. 闭包表达条件逻辑
4. 泛型约束
5. collect() 收集结果
6. Option/Result(可用于过滤错误结果)
7. 所有权与借用

这些特性分别解决了:

  • 遍历集合元素
  • 表达条件逻辑
  • 收集结果到新集合
  • 保证内存安全和数据所有权清晰

8. 性能与工程代价

  • 迭代器链本身是惰性求值,避免生成中间集合,性能接近手写循环。
  • 每次 filter() 都返回一个新的迭代器结构,但在编译优化后通常会内联,开销很小。
  • 使用 Box<dyn Iterator> 或动态闭包组合时会有一定的动态分发开销。
  • 如果链过长,调试和错误信息可能略复杂,但对运行时性能影响有限。

总结:Rust 过滤器模式在性能上通常非常高效,同时表达力和组合能力都强。


9. 典型应用场景

1. 筛选集合中满足条件的元素(用户、订单、记录)
2. 日志系统中筛选错误、警告记录
3. 数据流管道处理(网络数据、文件行、数据库查询结果)
4. 配合 map/fold 实现数据转换和聚合
5. 批量任务或事件流处理

在 Rust 项目中,过滤器模式非常适合数据处理、流式处理和集合转换。


10. 和相似模式的区别

10.1 与迭代器模式

  • 迭代器模式提供遍历能力
  • 过滤器模式在迭代器基础上增加条件筛选逻辑

10.2 与责任链模式

  • 责任链处理请求经过多个处理节点
  • 过滤器模式处理数据集合逐元素筛选

10.3 与访问者模式

  • 访问者模式关注在不修改结构的情况下增加操作
  • 过滤器模式关注条件筛选逻辑

11. 使用该模式的优点

1. 筛选逻辑与集合遍历解耦
2. 支持条件组合,灵活性高
3. 代码可读性和可维护性好
4. 可以与迭代器链式组合形成数据管道
5. 性能优异,支持惰性求值

12. 使用该模式的代价

1. 初学者不容易理解闭包 + 迭代器链
2. 链过长时可读性下降
3. 对复杂状态和依赖条件组合,调试略麻烦
4. 动态分发闭包或 Box<dyn Iterator> 有额外开销

13. 什么时候不应该使用

1. 数据量小,条件简单,直接循环即可
2. 业务逻辑涉及大量状态变化,迭代器链不直观
3. 调试和可读性优先于声明式管道
4. 不需要组合多个条件或链式处理

14. 一个简单例子思路

场景: 从用户列表中筛选出年龄大于 18 且状态为活跃的用户,并统计数量。

处理流程:

1. 数据源:Vec<User>
2. 遍历集合:iter()
3. 条件筛选:filter(|u| u.age > 18), filter(|u| u.active)
4. 收集或统计:count() 或 collect()
5. 输出结果

适合练习 Rust 特性:iter(), filter(), 闭包, collect(), count()


15. 总结一句话

过滤器模式的本质是:

将条件筛选逻辑抽象出来,使数据集合的筛选过程可以链式组合、与遍历逻辑解耦,并且在 Rust 中可以高效地构建数据处理管道。