1. 过滤器模式初探:从咖啡机到代码架构
第一次接触过滤器模式是在重构一个老旧的用户管理系统时。系统里有十几万用户数据,前端需要根据不同条件组合筛选——按地域、按活跃度、按会员等级。当时的代码里充斥着各种if-else嵌套,每次新增筛选条件都像在走钢丝。直到我把这些判断逻辑抽象成独立的过滤器对象,才真正体会到这种设计模式的精妙。
过滤器模式(Filter Pattern)本质上是对数据筛选过程的面向对象封装。就像咖啡机的滤网可以更换不同孔径的滤纸来处理不同颗粒度的咖啡粉,我们的代码里也可以组装不同的过滤器对象来处理数据集合。这种模式在Java集合操作、SQL语句生成、权限校验等场景尤为常见,Spring Security的过滤器链就是其经典实现。
关键认知:过滤器模式不是简单的条件判断封装,而是通过组合多个标准(Criteria)来实现灵活的筛选策略
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 模式结构与实现原理
2.1 标准UML类图解析
典型的过滤器模式包含三个核心角色:
- 被过滤对象(Target):可以是任意业务对象,如User、Product等
- 过滤器接口(Filter):定义统一的过滤方法,通常为
filter(List<T> items) - 具体过滤器(ConcreteFilter):实现特定过滤逻辑,如AgeFilter、GenderFilter等
java复制// 基础接口定义
public interface Filter<T> {
List<T> filter(List<T> items);
}
// 具体实现示例
public class ActiveUserFilter implements Filter<User> {
@Override
public List<User> filter(List<User> items) {
return items.stream()
.filter(user -> user.isActive())
.collect(Collectors.toList());
}
}
2.2 Java8下的优雅实现
利用Lambda表达式和Stream API,过滤器模式可以写得非常简洁:
java复制List<User> vipUsers = users.stream()
.filter(User::isVip)
.filter(user -> user.getAge() > 18)
.collect(Collectors.toList());
但这种写法存在局限性:无法复用过滤逻辑、难以动态组合条件。更专业的做法是构建过滤器组合器:
java复制public class FilterChain<T> implements Filter<T> {
private List<Filter<T>> filters = new ArrayList<>();
public FilterChain<T> addFilter(Filter<T> filter) {
filters.add(filter);
return this;
}
@Override
public List<T> filter(List<T> items) {
List<T> result = items;
for (Filter<T> filter : filters) {
result = filter.filter(result);
}
return result;
}
}
3. 实战:构建电商商品筛选系统
3.1 场景需求分析
假设我们需要为电商平台实现商品筛选功能,要求支持:
- 按价格区间筛选
- 按商品类目筛选
- 按库存状态筛选
- 组合条件查询(AND/OR逻辑)
3.2 具体实现步骤
基础过滤器实现:
java复制public class PriceRangeFilter implements Filter<Product> {
private final BigDecimal min;
private final BigDecimal max;
public PriceRangeFilter(BigDecimal min, BigDecimal max) {
this.min = min;
this.max = max;
}
@Override
public List<Product> filter(List<Product> items) {
return items.stream()
.filter(p -> p.getPrice().compareTo(min) >= 0)
.filter(p -> p.getPrice().compareTo(max) <= 0)
.collect(Collectors.toList());
}
}
组合过滤器实现:
java复制public class OrFilter<T> implements Filter<T> {
private final Filter<T> first;
private final Filter<T> second;
public OrFilter(Filter<T> first, Filter<T> second) {
this.first = first;
this.second = second;
}
@Override
public List<T> filter(List<T> items) {
List<T> firstResult = first.filter(items);
List<T> secondResult = second.filter(items);
return Stream.concat(firstResult.stream(), secondResult.stream())
.distinct()
.collect(Collectors.toList());
}
}
客户端调用示例:
java复制FilterChain<Product> filterChain = new FilterChain<>()
.addFilter(new PriceRangeFilter(new BigDecimal("100"), new BigDecimal("500")))
.addFilter(new CategoryFilter("电子产品"));
List<Product> results = filterChain.filter(allProducts);
4. 性能优化与高级技巧
4.1 延迟执行与短路优化
当处理大数据集时,可以通过Predicate组合实现延迟计算:
java复制public class PredicateFilter<T> implements Filter<T> {
private final Predicate<T> predicate;
public PredicateFilter(Predicate<T> predicate) {
this.predicate = predicate;
}
@Override
public List<T> filter(List<T> items) {
return items.stream()
.filter(predicate)
.collect(Collectors.toList());
}
}
// 构建复杂条件
Predicate<Product> complexPredicate = p -> p.getPrice() > 100
&& (p.getCategory().equals("电子产品") || p.getStock() > 0);
4.2 与Spring框架集成
在Spring应用中,可以结合工厂模式创建过滤器Bean:
java复制@Configuration
public class FilterConfig {
@Bean
@Scope(scopeName = "prototype")
public Filter<Product> priceFilter(
@Value("${filter.price.min}") double min,
@Value("${filter.price.max}") double max) {
return new PriceRangeFilter(BigDecimal.valueOf(min), BigDecimal.valueOf(max));
}
}
5. 模式对比与选用建议
5.1 与策略模式的区别
常有人混淆过滤器模式和策略模式,关键差异在于:
- 策略模式:针对单个对象的算法替换(如支付方式)
- 过滤器模式:针对对象集合的筛选操作
5.2 适用场景判断
推荐使用过滤器模式当:
- 需要支持动态组合的查询条件
- 筛选逻辑可能频繁变化
- 需要复用相同的筛选条件
- 系统需要支持多种查询标准
不适用的情况:
- 简单的单条件查询
- 对性能要求极高的实时系统
- 筛选逻辑极其稳定不变
6. 常见陷阱与最佳实践
6.1 内存泄漏风险
在实现过滤器链时,如果保存了原始集合的引用,可能导致内存无法释放:
java复制// 错误示例
public List<T> filter(List<T> items) {
this.lastInput = items; // 持有引用危险!
return doFilter(items);
}
6.2 线程安全问题
过滤器实现应当是无状态的,否则在并发场景下会出现问题:
java复制// 错误示例
public class BadFilter implements Filter<User> {
private List<User> cache; // 可变状态
@Override
public List<User> filter(List<User> items) {
if (cache == null) {
cache = doFilter(items);
}
return cache;
}
}
6.3 日志记录建议
为方便调试,可以在过滤器链中添加日志记录:
java复制public class LoggingFilter<T> implements Filter<T> {
private final Filter<T> delegate;
public LoggingFilter(Filter<T> delegate) {
this.delegate = delegate;
}
@Override
public List<T> filter(List<T> items) {
System.out.println("Before filtering: " + items.size());
List<T> result = delegate.filter(items);
System.out.println("After filtering: " + result.size());
return result;
}
}
7. 现代框架中的过滤器模式
7.1 Java Stream API的filter方法
Java8的Stream API本质上是过滤器模式的函数式实现:
java复制List<String> filtered = list.stream()
.filter(s -> s.startsWith("A"))
.filter(s -> s.length() > 3)
.collect(Collectors.toList());
7.2 Spring Security的过滤器链
Spring Security的核心机制就是过滤器链:
java复制http.addFilterBefore(new CustomFilter(), BasicAuthenticationFilter.class)
.addFilterAfter(new AuditFilter(), SecurityContextPersistenceFilter.class);
7.3 MyBatis的Interceptor机制
MyBatis通过Interceptor接口实现SQL操作的过滤:
java复制@Intercepts({
@Signature(type= Executor.class, method="query",
args={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class QueryInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 对查询进行过滤处理
return invocation.proceed();
}
}
8. 测试策略与Mock技巧
8.1 单元测试过滤器
使用AssertJ可以方便地测试过滤器:
java复制@Test
void testPriceFilter() {
List<Product> products = Arrays.asList(
new Product("p1", new BigDecimal("50")),
new Product("p2", new BigDecimal("150"))
);
Filter<Product> filter = new PriceRangeFilter(new BigDecimal("100"), new BigDecimal("200"));
List<Product> result = filter.filter(products);
assertThat(result).extracting(Product::getId)
.containsExactly("p2");
}
8.2 模拟大数据集测试
使用随机数据生成器测试性能:
java复制@Test
void testPerformance() {
List<Product> largeList = IntStream.range(0, 100000)
.mapToObj(i -> new Product("p" + i,
new BigDecimal(ThreadLocalRandom.current().nextInt(10, 1000))))
.collect(Collectors.toList());
FilterChain<Product> chain = new FilterChain<>()
.addFilter(new PriceRangeFilter(new BigDecimal("100"), new BigDecimal("500")))
.addFilter(new StockFilter(true));
long start = System.currentTimeMillis();
List<Product> result = chain.filter(largeList);
long duration = System.currentTimeMillis() - start;
assertThat(duration).isLessThan(100);
}
9. 扩展思考:函数式与面向对象的融合
现代Java开发中,可以结合两种范式:
java复制public class FunctionalFilter<T> implements Filter<T> {
private final Function<List<T>, List<T>> function;
public FunctionalFilter(Function<List<T>, List<T>> function) {
this.function = function;
}
@Override
public List<T> filter(List<T> items) {
return function.apply(items);
}
}
// 使用示例
Filter<User> filter = new FunctionalFilter<>(list ->
list.stream()
.filter(User::isActive)
.filter(u -> u.getAge() > 18)
.collect(Collectors.toList()));
这种实现既保持了面向对象的接口规范,又利用了函数式编程的灵活性。在实际项目中,我通常会根据团队的技术偏好选择适合的实现方式——对函数式熟悉的团队可以采用更简洁的Lambda风格,而传统Java团队可能更适合标准的面向对象实现。
