List
for (Article article : articles) {
if (article.getTitle() != null && article.getTitle().contains(keyword)) {
result.add(article);
}
}
return result;
}
java
public List
List
for (Article article : articles) {
if (article.getTitle() != null && article.getTitle().contains(keyword)) {
result.add(article);
}
}
return result;
}
public List
List
for (Article article : articles) {
if (article.getDescription() != null && !article.getDescription().isEmpty()) {
result.add(article);
}
}
return result;
}
public List
List
for (Article article : articles) {
if (article.getContent() != null && article.getContent().length() > 1000) {
result.add(article);
}
}
return result;
}
观察这三个方法,除了内部的if条件不同,其余结构完全一致——遍历集合、判断条件、添加符合条件的元素。这种高度相似的结构正是重构的最佳目标。
此时,我们可以引入Predicate<T>接口。Predicate是一个函数式接口,接受一个参数并返回布尔值,非常适合用来抽象“判断条件”这一行为。通过将变化的部分——即判断逻辑——封装为Predicate<Article>,我们可以将三个方法合并为一个通用的过滤方法:
java
public List<Article> filterArticles(List<Article> articles, Predicate<Article> condition) {
List<Article> result = new ArrayList<>();
for (Article article : articles) {
if (condition.test(article)) {
result.add(article);
}
}
return result;
}
这样一来,原本分散的三个方法可以通过传入不同的Predicate实例来实现:
java
// 按标题包含关键词过滤
List
a -> a.getTitle() != null && a.getTitle().contains("Java"));
// 按描述非空过滤
List
a -> a.getDescription() != null && !a.getDescription().isEmpty());
// 按正文长度超过1000字过滤
List
a -> a.getContent() != null && a.getContent().length() > 1000);
这种重构方式的优势显而易见。首先,代码量大幅减少,逻辑更加集中;其次,扩展性显著增强——新增一种过滤规则只需提供一个新的Predicate,无需再复制粘贴整个方法结构;最后,代码可读性提升,调用者能清晰地看到“我正在用什么条件过滤”。
更进一步,结合Java 8的Stream API,我们甚至可以将过滤逻辑写得更加简洁:
java
public List<Article> filterArticles(List<Article> articles, Predicate<Article> condition) {
return articles.stream()
.filter(condition)
.collect(Collectors.toList());
}
这不仅使代码更短,也更具函数式编程的表达力。更重要的是,这种基于Predicate的抽象方式并不局限于过滤场景。任何存在“单一条件差异”的重复逻辑,例如对象校验、数据转换前的预判、权限检查等,都可以通过类似手段进行统一处理。
总之,面对因单行差异导致的代码重复,Predicate提供了一种轻量级、高内聚的解决方案。它不仅帮助我们写出更少的代码,更重要的是促使我们思考如何将变化与稳定分离,从而构建出更易于维护和扩展的系统。