Skip to content

Commit 61a819a

Browse files
committed
Added fixes after review. Changed example pattern application to threat detection domain
1 parent 905b5dc commit 61a819a

18 files changed

+568
-332
lines changed

filterer/README.MD

Lines changed: 164 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
--- # this is so called 'Yaml Front Matter', read up on it here: http://jekyllrb.com/docs/frontmatter/
22
layout: pattern
3-
title: Filterer Pattern
3+
title: Filterer
44
folder: filterer
55
permalink: /patterns/filterer/
66
description: Design pattern that helps container-like objects to return filtered version of themselves.# short meta description that shows in Google search results
@@ -11,15 +11,173 @@ tags:
1111
---
1212

1313
## Name / classification
14-
Filterer Pattern
14+
Filterer
1515

1616
## Intent
1717
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.
1818

1919
## Explanation
20-
The container-like object needs to have a method that returns an instance of `Filterer`. This helper interface gives
20+
Real world example
21+
22+
> We are designing a threat(malware) detection system. We can have different types of threats and systems. We have a requirement that
23+
> 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
24+
> 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).
25+
>
26+
27+
In plain words
28+
29+
> We need to be able to filter different types of systems(container-like objects) based on properties of Threats that they contain.
30+
> Adding new properties for Threats should be easy (we still need the ability to filter by those new properties).
31+
32+
**Programmatic Example**
33+
34+
To model the threat detection example presented above we introduce `Threat` and `ThreatAwareSystem` interfaces.
35+
36+
```java
37+
public interface Threat {
38+
String name();
39+
int id();
40+
ThreatType type();
41+
}
42+
43+
public interface ThreatAwareSystem {
44+
String systemId();
45+
List<? extends Threat> threats();
46+
Filterer<? extends ThreatAwareSystem, ? extends Threat> filtered();
47+
48+
}
49+
```
50+
Notice the `filtered` method that returns instance of `Filterer` interface which is defined as :
51+
```java
52+
@FunctionalInterface
53+
public interface Filterer<G, E> {
54+
G by(Predicate<? super E> predicate);
55+
}
56+
```
57+
it is used to fulfill the requirement for system to be able to filter itself based on threat properties.
58+
The container-like object (`ThreatAwareSystem` in our case) needs to have a method that returns an instance of `Filterer`. This helper interface gives
2159
ability to covariantly specify a lower bound of contravariant `Predicate` in the subinterfaces of interfaces representing the container-like objects.
2260

61+
In our example we will be able to pass a predicate that takes `? extends Threat` object and return `? extends ThreatAwareSystem`
62+
from `Filtered::by` method. A simple implementation of `ThreadAwareSystem` :
63+
```java
64+
public class SimpleThreatAwareSystem implements ThreatAwareSystem {
65+
66+
private final String systemId;
67+
private final ImmutableList<Threat> issues;
68+
69+
public SimpleThreatAwareSystem(final String systemId, final List<Threat> issues) {
70+
this.systemId = systemId;
71+
this.issues = ImmutableList.copyOf(issues);
72+
}
73+
74+
@Override
75+
public String systemId() {
76+
return systemId;
77+
}
78+
79+
@Override
80+
public List<? extends Threat> threats() {
81+
return new ArrayList<>(issues);
82+
}
83+
84+
@Override
85+
public Filterer<? extends ThreatAwareSystem, ? extends Threat> filtered() {
86+
return this::filteredGroup;
87+
}
88+
89+
private ThreatAwareSystem filteredGroup(Predicate<? super Threat> predicate) {
90+
return new SimpleThreatAwareSystem(this.systemId, filteredItems(predicate));
91+
}
92+
93+
private List<Threat> filteredItems(Predicate<? super Threat> predicate) {
94+
return this.issues.stream()
95+
.filter(predicate)
96+
.collect(Collectors.toList());
97+
}
98+
}
99+
```
100+
the `filtered` method is overridden to filter the threats list by given predicate.
101+
102+
Now if we introduce new subtype of `Thread` interface that adds probability with which given thread can appear :
103+
```java
104+
public interface ProbableThreat extends Threat {
105+
double probability();
106+
}
107+
```
108+
we can also introduce a new interface that represents a system that is aware of threats with their probabilities :
109+
````java
110+
public interface ProbabilisticThreatAwareSystem extends ThreatAwareSystem {
111+
@Override
112+
List<? extends ProbableThreat> threats();
113+
114+
@Override
115+
Filterer<? extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> filtered();
116+
}
117+
````
118+
Notice how we override the `filtered` method in `ProbabilisticThreatAwareSystem` and specify different return covariant type
119+
by specifing different generic types. Our interfaces are clean and not cluttered by default implementations. We
120+
we will be able to filter `ProbabilisticThreatAwareSystem` by `ProbableThreat` properties :
121+
```java
122+
public class SimpleProbabilisticThreatAwareSystem implements ProbabilisticThreatAwareSystem {
123+
124+
private final String systemId;
125+
private final ImmutableList<ProbableThreat> threats;
126+
127+
public SimpleProbabilisticThreatAwareSystem(final String systemId, final List<ProbableThreat> threats) {
128+
this.systemId = systemId;
129+
this.threats = ImmutableList.copyOf(threats);
130+
}
131+
132+
@Override
133+
public String systemId() {
134+
return systemId;
135+
}
136+
137+
@Override
138+
public List<? extends ProbableThreat> threats() {
139+
return threats;
140+
}
141+
142+
@Override
143+
public Filterer<? extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> filtered() {
144+
return this::filteredGroup;
145+
}
146+
147+
private ProbabilisticThreatAwareSystem filteredGroup(final Predicate<? super ProbableThreat> predicate) {
148+
return new SimpleProbabilisticThreatAwareSystem(this.systemId, filteredItems(predicate));
149+
}
150+
151+
private List<ProbableThreat> filteredItems(final Predicate<? super ProbableThreat> predicate) {
152+
return this.threats.stream()
153+
.filter(predicate)
154+
.collect(Collectors.toList());
155+
}
156+
}
157+
```
158+
159+
Now if we want filter `ThreatAwareSystem` by threat type we can do :
160+
```java
161+
Threat rootkit = new SimpleThreat(ThreatType.ROOTKIT, 1, "Simple-Rootkit");
162+
Threat trojan = new SimpleThreat(ThreatType.TROJAN, 2, "Simple-Trojan");
163+
List<Threat> threats = List.of(rootkit, trojan);
164+
165+
ThreatAwareSystem threatAwareSystem = new SimpleThreatAwareSystem("System-1", threats);
166+
167+
ThreatAwareSystem rootkitThreatAwareSystem = threatAwareSystem.filtered()
168+
.by(threat -> threat.type() == ThreatType.ROOTKIT);
169+
```
170+
or if we want to filter `ProbabilisticThreatAwareSystem` :
171+
```java
172+
ProbableThreat malwareTroyan = new SimpleProbableThreat("Troyan-ArcBomb", 1, ThreatType.TROJAN, 0.99);
173+
ProbableThreat rootkit = new SimpleProbableThreat("Rootkit-System", 2, ThreatType.ROOTKIT, 0.8);
174+
List<ProbableThreat> probableThreats = List.of(malwareTroyan, rootkit);
175+
176+
ProbabilisticThreatAwareSystem simpleProbabilisticThreatAwareSystem =new SimpleProbabilisticThreatAwareSystem("System-1", probableThreats);
177+
178+
ProbabilisticThreatAwareSystem filtered = simpleProbabilisticThreatAwareSystem.filtered()
179+
.by(probableThreat -> Double.compare(probableThreat.probability(), 0.99) == 0);
180+
```
23181
## Class diagram
24182
![Filterer](./etc/filterer.png "Filterer")
25183

@@ -34,11 +192,11 @@ It enables you to easily extend filtering ability of container-like objects as b
34192
## Known uses
35193
One of the uses is present on the blog presented in this link. It presents how to use `Filterer` pattern to create text issue anaylyzer with support for test cases used for unit testing.
36194

37-
## Consequences (the good and the bad, add criticism here)
38-
Good :
195+
## Consequences
196+
Pros :
39197
* 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.
40198

41-
Bad :
199+
Cons :
42200
* covariant return types mixed with generics can be sometimes tricky
43201

44202
## Credits

filterer/etc/filterer.png

-10.7 KB
Loading

0 commit comments

Comments
 (0)