Java 中的过滤器模式:使用动态过滤器简化数据处理
也称为
- 过滤器
- 管道和过滤器
过滤器设计模式的意图
Java 中的过滤器设计模式对于创建动态且可扩展的过滤解决方案至关重要。此模式允许将一系列过滤器应用于数据对象,从而提高数据处理的灵活性和可扩展性。
带有真实世界示例的过滤器模式详细解释
真实世界示例
想象一个图书馆系统采用过滤器模式来动态组合过滤器条件,例如类型、作者和可用性。此 Java 模式使系统更易于维护和扩展。图书馆系统使用过滤器设计模式,而不是为每种可能的条件组合编写单独的方法。每个过滤器条件都封装为一个对象,这些过滤器对象可以在运行时动态组合以创建复杂的过滤逻辑。例如,用户可以通过组合可用性过滤器和出版年份过滤器来搜索既可用又在 2010 年后出版的书籍。这种方法使系统更加灵活,并且更易于维护,因为可以添加新的过滤条件而无需修改现有代码。
简单来说
过滤器模式是一种设计模式,它帮助类似容器的对象返回其过滤后的版本。
Java 中过滤器模式的编程示例
为了说明,我们使用 Java 中的过滤器设计模式来构建一个恶意软件检测系统。此系统可以根据各种条件过滤威胁,展示了该模式的灵活性 and 动态特性。在设计中,我们必须考虑到以后可以添加新的威胁类型。此外,还有一个要求,即威胁检测系统可以根据不同的条件过滤检测到的威胁(目标系统充当威胁的类似容器的对象)。
为了对威胁检测系统进行建模,我们引入了 Threat
和 ThreatAwareSystem
接口。
public interface Threat {
String name();
int id();
ThreatType type();
}
public interface ThreatAwareSystem {
String systemId();
List<? extends Threat> threats();
Filterer<? extends ThreatAwareSystem, ? extends Threat> filtered();
}
请注意 filtered
方法,该方法返回 Filterer
接口的实例,该接口定义为
@FunctionalInterface
public interface Filterer<G, E> {
G by(Predicate<? super E> predicate);
}
ThreatAwareSystem
的简单实现
public class SimpleThreatAwareSystem implements ThreatAwareSystem {
private final String systemId;
private final ImmutableList<Threat> issues;
public SimpleThreatAwareSystem(final String systemId, final List<Threat> issues) {
this.systemId = systemId;
this.issues = ImmutableList.copyOf(issues);
}
@Override
public String systemId() {
return systemId;
}
@Override
public List<? extends Threat> threats() {
return new ArrayList<>(issues);
}
@Override
public Filterer<? extends ThreatAwareSystem, ? extends Threat> filtered() {
return this::filteredGroup;
}
private ThreatAwareSystem filteredGroup(Predicate<? super Threat> predicate) {
return new SimpleThreatAwareSystem(this.systemId, filteredItems(predicate));
}
private List<Threat> filteredItems(Predicate<? super Threat> predicate) {
return this.issues.stream()
.filter(predicate)
.collect(Collectors.toList());
}
}
现在,如果我们引入 Threat
接口的一个新子类型,该子类型添加了给定威胁出现的概率
public interface ProbableThreat extends Threat {
double probability();
}
我们还可以引入一个新接口,该接口表示一个了解威胁及其概率的系统
public interface ProbabilisticThreatAwareSystem extends ThreatAwareSystem {
@Override
List<? extends ProbableThreat> threats();
@Override
Filterer<? extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> filtered();
}
我们将能够通过 ProbableThreat
属性过滤 ProbabilisticThreatAwareSystem
public class SimpleProbabilisticThreatAwareSystem implements ProbabilisticThreatAwareSystem {
private final String systemId;
private final ImmutableList<ProbableThreat> threats;
public SimpleProbabilisticThreatAwareSystem(final String systemId, final List<ProbableThreat> threats) {
this.systemId = systemId;
this.threats = ImmutableList.copyOf(threats);
}
@Override
public String systemId() {
return systemId;
}
@Override
public List<? extends ProbableThreat> threats() {
return threats;
}
@Override
public Filterer<? extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> filtered() {
return this::filteredGroup;
}
private ProbabilisticThreatAwareSystem filteredGroup(final Predicate<? super ProbableThreat> predicate) {
return new SimpleProbabilisticThreatAwareSystem(this.systemId, filteredItems(predicate));
}
private List<ProbableThreat> filteredItems(final Predicate<? super ProbableThreat> predicate) {
return this.threats.stream()
.filter(predicate)
.collect(Collectors.toList());
}
}
现在让我们看看完整的示例,该示例演示了过滤器模式在实践中的工作原理。
@Slf4j
public class App {
public static void main(String[] args) {
filteringSimpleThreats();
filteringSimpleProbableThreats();
}
private static void filteringSimpleProbableThreats() {
LOGGER.info("### Filtering ProbabilisticThreatAwareSystem by probability ###");
var trojanArcBomb = new SimpleProbableThreat("Trojan-ArcBomb", 1, ThreatType.TROJAN, 0.99);
var rootkit = new SimpleProbableThreat("Rootkit-Kernel", 2, ThreatType.ROOTKIT, 0.8);
List<ProbableThreat> probableThreats = List.of(trojanArcBomb, rootkit);
var probabilisticThreatAwareSystem =
new SimpleProbabilisticThreatAwareSystem("Sys-1", probableThreats);
LOGGER.info("Filtering ProbabilisticThreatAwareSystem. Initial : "
+ probabilisticThreatAwareSystem);
//Filtering using filterer
var filteredThreatAwareSystem = probabilisticThreatAwareSystem.filtered()
.by(probableThreat -> Double.compare(probableThreat.probability(), 0.99) == 0);
LOGGER.info("Filtered by probability = 0.99 : " + filteredThreatAwareSystem);
}
private static void filteringSimpleThreats() {
LOGGER.info("### Filtering ThreatAwareSystem by ThreatType ###");
var rootkit = new SimpleThreat(ThreatType.ROOTKIT, 1, "Simple-Rootkit");
var trojan = new SimpleThreat(ThreatType.TROJAN, 2, "Simple-Trojan");
List<Threat> threats = List.of(rootkit, trojan);
var threatAwareSystem = new SimpleThreatAwareSystem("Sys-1", threats);
LOGGER.info("Filtering ThreatAwareSystem. Initial : " + threatAwareSystem);
//Filtering using Filterer
var rootkitThreatAwareSystem = threatAwareSystem.filtered()
.by(threat -> threat.type() == ThreatType.ROOTKIT);
LOGGER.info("Filtered by threatType = ROOTKIT : " + rootkitThreatAwareSystem);
}
}
运行示例会生成以下控制台输出。
08:33:23.568 [main] INFO com.iluwatar.filterer.App -- ### Filtering ThreatAwareSystem by ThreatType ###
08:33:23.574 [main] INFO com.iluwatar.filterer.App -- Filtering ThreatAwareSystem. Initial : SimpleThreatAwareSystem(systemId=Sys-1, issues=[SimpleThreat(threatType=ROOTKIT, id=1, name=Simple-Rootkit), SimpleThreat(threatType=TROJAN, id=2, name=Simple-Trojan)])
08:33:23.576 [main] INFO com.iluwatar.filterer.App -- Filtered by threatType = ROOTKIT : SimpleThreatAwareSystem(systemId=Sys-1, issues=[SimpleThreat(threatType=ROOTKIT, id=1, name=Simple-Rootkit)])
08:33:23.576 [main] INFO com.iluwatar.filterer.App -- ### Filtering ProbabilisticThreatAwareSystem by probability ###
08:33:23.581 [main] INFO com.iluwatar.filterer.App -- Filtering ProbabilisticThreatAwareSystem. Initial : SimpleProbabilisticThreatAwareSystem(systemId=Sys-1, threats=[SimpleProbableThreat{probability=0.99} SimpleThreat(threatType=TROJAN, id=1, name=Trojan-ArcBomb), SimpleProbableThreat{probability=0.8} SimpleThreat(threatType=ROOTKIT, id=2, name=Rootkit-Kernel)])
08:33:23.581 [main] INFO com.iluwatar.filterer.App -- Filtered by probability = 0.99 : SimpleProbabilisticThreatAwareSystem(systemId=Sys-1, threats=[SimpleProbableThreat{probability=0.99} SimpleThreat(threatType=TROJAN, id=1, name=Trojan-ArcBomb)])
何时在 Java 中使用过滤器模式
- 当需要对对象集合进行动态和灵活的过滤时,请使用过滤器模式。
- 此 Java 设计模式非常适合过滤逻辑经常发生变化或需要以各种方式组合的应用程序。
- 非常适合需要将过滤逻辑与核心业务逻辑分离的场景。
过滤器模式 Java 教程
Java 中过滤器模式的真实世界应用
- Java 中的流处理库,例如 Apache Kafka Streams,使用此模式来构建复杂的数据处理管道。
- 图像处理软件通常使用过滤器来按顺序对图像应用效果或转换。
过滤器模式的优势和权衡
优点
- 通过允许添加或重新组织不同的过滤器而不影响系统的其他部分来提高灵活性。
- 增强可测试性,因为过滤器可以独立测试。
- 促进数据处理各个阶段之间的松耦合。
权衡
- 过滤器之间连续数据传递可能会导致潜在的性能开销。
- 随着过滤器数量的增加,复杂性也会增加,这可能会影响可维护性。
相关的 Java 设计模式
- 责任链:过滤器可以看作责任链的一种特殊形式,其中每个过滤器都决定是否以及如何处理输入数据以及是否将其传递给链中。
- 装饰器:类似于装饰器,因为两者都动态地修改行为;但是,过滤器更关注数据转换而不是添加职责。