README.MD 7.9 KB
Newer Older
M
Michal Krzywanski 已提交
1 2
--- # this is so called 'Yaml Front Matter', read up on it here: http://jekyllrb.com/docs/frontmatter/
layout: pattern
3
title: Filterer
M
Michal Krzywanski 已提交
4 5 6 7 8 9 10 11 12 13
folder: filterer
permalink: /patterns/filterer/
description: Design pattern that helps container-like objects to return filtered version of themselves.# short meta description that shows in Google search results
categories:
 - Functional
tags:
 - Extensibility
---

## Name / classification
14
Filterer
M
Michal Krzywanski 已提交
15 16 17 18 19

## Intent
The intent of this design pattern is to to introduce a functional interface that will add a functionality for container-like objects to easily return filtered versions of themselves.

## Explanation
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
Real world example

> We are designing a threat(malware) detection system. We can have different types of threats and systems. We have a requirement that
> system should be aware of threats that are present in it. In the design we have to take into consideration that new Threat types can be
> added later. Also there is a requirement that a system can filter itself based on the threats that it possesses (system acts as container-like object for threats).
>

In plain words

> We need to be able to filter different types of systems(container-like objects) based on properties of Threats that they contain.
> Adding new properties for Threats should be easy (we still need the ability to filter by those new properties).

**Programmatic Example**

To model the threat detection example presented above we introduce `Threat` and `ThreatAwareSystem` interfaces.

```java
public interface Threat {
  String name();
  int id();
  ThreatType type();
}

public interface ThreatAwareSystem {
  String systemId();
  List<? extends Threat> threats();
  Filterer<? extends ThreatAwareSystem, ? extends Threat> filtered();

}
```
Notice the `filtered` method that returns instance of `Filterer` interface which is defined as :
```java
@FunctionalInterface
public interface Filterer<G, E> {
  G by(Predicate<? super E> predicate);
}
```
it is used to fulfill the requirement for system to be able to filter itself based on threat properties.
The container-like object (`ThreatAwareSystem` in our case) needs to have a method that returns an instance of `Filterer`. This helper interface gives 
M
Michal Krzywanski 已提交
59 60
ability to covariantly specify a lower bound of contravariant `Predicate` in the subinterfaces of interfaces representing the container-like objects.

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
In our example we will be able to pass a predicate that takes `? extends Threat` object and return `?  extends ThreatAwareSystem`
from `Filtered::by` method. A simple implementation of `ThreadAwareSystem` :
```java
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());
  }
}
```
the `filtered` method is overridden to filter the threats list by given predicate.

Now if we introduce new subtype of `Thread` interface that adds probability with which given thread can appear :
```java
public interface ProbableThreat extends Threat {
  double probability();
}
```
we can also introduce a new interface that represents a system that is aware of threats with their probabilities :
````java
public interface ProbabilisticThreatAwareSystem extends ThreatAwareSystem {
  @Override
  List<? extends ProbableThreat> threats();

  @Override
  Filterer<? extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> filtered();
}
````
Notice how we override the `filtered` method in `ProbabilisticThreatAwareSystem` and specify different return covariant type
by specifing different generic types. Our interfaces are clean and not cluttered by default implementations. We
we will be able to filter `ProbabilisticThreatAwareSystem` by `ProbableThreat` properties :
```java
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());
  }
}
```

Now if we want filter `ThreatAwareSystem` by threat type we can do :
```java
Threat rootkit = new SimpleThreat(ThreatType.ROOTKIT, 1, "Simple-Rootkit");
Threat trojan = new SimpleThreat(ThreatType.TROJAN, 2, "Simple-Trojan");
List<Threat> threats = List.of(rootkit, trojan);

ThreatAwareSystem threatAwareSystem = new SimpleThreatAwareSystem("System-1", threats);

ThreatAwareSystem rootkitThreatAwareSystem = threatAwareSystem.filtered()
           .by(threat -> threat.type() == ThreatType.ROOTKIT);
```
or if we want to filter `ProbabilisticThreatAwareSystem` :
```java
ProbableThreat malwareTroyan = new SimpleProbableThreat("Troyan-ArcBomb", 1, ThreatType.TROJAN, 0.99);
ProbableThreat rootkit = new SimpleProbableThreat("Rootkit-System", 2, ThreatType.ROOTKIT, 0.8);
List<ProbableThreat> probableThreats = List.of(malwareTroyan, rootkit);

ProbabilisticThreatAwareSystem simpleProbabilisticThreatAwareSystem =new SimpleProbabilisticThreatAwareSystem("System-1", probableThreats);

ProbabilisticThreatAwareSystem filtered = simpleProbabilisticThreatAwareSystem.filtered()
           .by(probableThreat -> Double.compare(probableThreat.probability(), 0.99) == 0);
```
M
Michal Krzywanski 已提交
181 182 183 184 185 186 187 188 189 190 191 192
## Class diagram
![Filterer](./etc/filterer.png "Filterer")

## Applicability
Pattern can be used when working with container-like objects that use subtyping, instead of parametrizing(generics) for extensible class structure.
It enables you to easily extend filtering ability of container-like objects as business requirements change.

## Tutorials
* [Article about Filterer pattern posted on it's author's blog](https://blog.tlinkowski.pl/2018/filterer-pattern/)
* [Application of Filterer pattern in domain of text analysis](https://www.javacodegeeks.com/2019/02/filterer-pattern-10-steps.html)

## Known uses
M
Michal Krzywanski 已提交
193 194
One of the uses is present on the blog presented in [this](https://www.javacodegeeks.com/2019/02/filterer-pattern-10-steps.html) link. 
It presents how to use `Filterer` pattern to create text issue analyzer with support for test cases used for unit testing.
M
Michal Krzywanski 已提交
195

196 197
## Consequences
Pros :
M
Michal Krzywanski 已提交
198 199
 * you can easily introduce new subtypes for container-like objects and subtypes for objects that are contained within them and still be able to filter easily be new properties of those new subtypes.

200
Cons :
M
Michal Krzywanski 已提交
201 202 203 204
 * covariant return types mixed with generics can be sometimes tricky

## Credits
* Author of the pattern : [Tomasz Linkowski](https://tlinkowski.pl/)