Skip to content

Commit 6d38534

Browse files
committed
docs: update filterer
1 parent 6a97047 commit 6d38534

File tree

6 files changed

+134
-139
lines changed

6 files changed

+134
-139
lines changed

filterer/README.md

Lines changed: 128 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,203 +1,192 @@
11
---
22
title: Filterer
33
language: en
4-
category: Functional
4+
category: Behavioral
55
tag:
6-
- Extensibility
6+
- Data processing
7+
- Data transformation
8+
- Decoupling
9+
- Performance
10+
- Runtime
711
---
812

9-
## Name / classification
13+
## Also known as
1014

11-
Filterer
15+
* Filters
16+
* Pipes and Filters
1217

1318
## Intent
1419

15-
The intent of this design pattern is to introduce a functional interface that will add a
16-
functionality for container-like objects to easily return filtered versions of themselves.
20+
The Filterer pattern aims to apply a series of filters to data objects, where each filter processes the data based on specific rules and criteria, and passes the data to the next filter in the sequence.
1721

1822
## Explanation
1923

2024
Real world example
2125

22-
> We are designing a threat (malware) detection software which can analyze target systems for
23-
> threats that are present in it. In the design we have to take into consideration that new
24-
> Threat types can be added later. Additionally, there is a requirement that the threat detection
25-
> system can filter the detected threats based on different criteria (the target system acts as
26-
> container-like object for threats).
26+
> We are designing a threat (malware) detection software which can analyze target systems for threats that are present in it. In the design we have to take into consideration that new Threat types can be added later. Additionally, there is a requirement that the threat detection system can filter the detected threats based on different criteria (the target system acts as container-like object for threats).
2727
2828
In plain words
2929

30-
> Filterer pattern is a design pattern that helps container-like objects return filtered versions
31-
> of themselves.
30+
> Filterer pattern is a design pattern that helps container-like objects return filtered versions of themselves.
3231
3332
**Programmatic Example**
3433

35-
To model the threat detection example presented above we introduce `Threat` and `ThreatAwareSystem`
36-
interfaces.
34+
To model the threat detection example presented above we introduce `Threat` and `ThreatAwareSystem` interfaces.
3735

3836
```java
3937
public interface Threat {
40-
String name();
41-
int id();
42-
ThreatType type();
38+
String name();
39+
40+
int id();
41+
42+
ThreatType type();
4343
}
4444

4545
public interface ThreatAwareSystem {
46-
String systemId();
47-
List<? extends Threat> threats();
48-
Filterer<? extends ThreatAwareSystem, ? extends Threat> filtered();
46+
String systemId();
4947

48+
List<? extends Threat> threats();
49+
50+
Filterer<? extends ThreatAwareSystem, ? extends Threat> filtered();
5051
}
5152
```
5253

5354
Notice the `filtered` method that returns instance of `Filterer` interface which is defined as:
5455

5556
```java
57+
5658
@FunctionalInterface
5759
public interface Filterer<G, E> {
58-
G by(Predicate<? super E> predicate);
60+
G by(Predicate<? super E> predicate);
5961
}
6062
```
6163

62-
It is used to fulfill the requirement for system to be able to filter itself based on threat
63-
properties. The container-like object (`ThreatAwareSystem` in our case) needs to have a method that
64-
returns an instance of `Filterer`. This helper interface gives ability to covariantly specify a
65-
lower bound of contravariant `Predicate` in the subinterfaces of interfaces representing the
66-
container-like objects.
64+
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 ability to covariantly specify a lower bound of contravariant `Predicate` in the subinterfaces of interfaces representing the container-like objects.
6765

68-
In our example we will be able to pass a predicate that takes `? extends Threat` object and
69-
return `? extends ThreatAwareSystem` from `Filtered::by` method. A simple implementation
70-
of `ThreatAwareSystem`:
66+
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 `ThreatAwareSystem`:
7167

7268
```java
7369
public class SimpleThreatAwareSystem implements ThreatAwareSystem {
7470

75-
private final String systemId;
76-
private final ImmutableList<Threat> issues;
77-
78-
public SimpleThreatAwareSystem(final String systemId, final List<Threat> issues) {
79-
this.systemId = systemId;
80-
this.issues = ImmutableList.copyOf(issues);
81-
}
82-
83-
@Override
84-
public String systemId() {
85-
return systemId;
86-
}
87-
88-
@Override
89-
public List<? extends Threat> threats() {
90-
return new ArrayList<>(issues);
91-
}
92-
93-
@Override
94-
public Filterer<? extends ThreatAwareSystem, ? extends Threat> filtered() {
95-
return this::filteredGroup;
96-
}
97-
98-
private ThreatAwareSystem filteredGroup(Predicate<? super Threat> predicate) {
99-
return new SimpleThreatAwareSystem(this.systemId, filteredItems(predicate));
100-
}
101-
102-
private List<Threat> filteredItems(Predicate<? super Threat> predicate) {
103-
return this.issues.stream()
104-
.filter(predicate)
105-
.collect(Collectors.toList());
106-
}
71+
private final String systemId;
72+
private final ImmutableList<Threat> issues;
73+
74+
public SimpleThreatAwareSystem(final String systemId, final List<Threat> issues) {
75+
this.systemId = systemId;
76+
this.issues = ImmutableList.copyOf(issues);
77+
}
78+
79+
@Override
80+
public String systemId() {
81+
return systemId;
82+
}
83+
84+
@Override
85+
public List<? extends Threat> threats() {
86+
return new ArrayList<>(issues);
87+
}
88+
89+
@Override
90+
public Filterer<? extends ThreatAwareSystem, ? extends Threat> filtered() {
91+
return this::filteredGroup;
92+
}
93+
94+
private ThreatAwareSystem filteredGroup(Predicate<? super Threat> predicate) {
95+
return new SimpleThreatAwareSystem(this.systemId, filteredItems(predicate));
96+
}
97+
98+
private List<Threat> filteredItems(Predicate<? super Threat> predicate) {
99+
return this.issues.stream()
100+
.filter(predicate)
101+
.collect(Collectors.toList());
102+
}
107103
}
108104
```
109105

110106
The `filtered` method is overridden to filter the threats list by given predicate.
111107

112-
Now if we introduce a new subtype of `Threat` interface that adds probability with which given
113-
threat can appear:
108+
Now if we introduce a new subtype of `Threat` interface that adds probability with which given threat can appear:
114109

115110
```java
116111
public interface ProbableThreat extends Threat {
117-
double probability();
112+
double probability();
118113
}
119114
```
120115

121-
We can also introduce a new interface that represents a system that is aware of threats with their
122-
probabilities:
116+
We can also introduce a new interface that represents a system that is aware of threats with their probabilities:
123117

124118
````java
125119
public interface ProbabilisticThreatAwareSystem extends ThreatAwareSystem {
126-
@Override
127-
List<? extends ProbableThreat> threats();
120+
@Override
121+
List<? extends ProbableThreat> threats();
128122

129-
@Override
130-
Filterer<? extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> filtered();
123+
@Override
124+
Filterer<? extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> filtered();
131125
}
132126
````
133127

134-
Notice how we override the `filtered` method in `ProbabilisticThreatAwareSystem` and specify
135-
different return covariant type by specifying different generic types. Our interfaces are clean and
136-
not cluttered by default implementations. We we will be able to filter
137-
`ProbabilisticThreatAwareSystem` by `ProbableThreat` properties:
128+
Notice how we override the `filtered` method in `ProbabilisticThreatAwareSystem` and specify different return covariant type by specifying different generic types. Our interfaces are clean and not cluttered by default implementations. We will be able to filter `ProbabilisticThreatAwareSystem` by `ProbableThreat` properties:
138129

139130
```java
140131
public class SimpleProbabilisticThreatAwareSystem implements ProbabilisticThreatAwareSystem {
141132

142-
private final String systemId;
143-
private final ImmutableList<ProbableThreat> threats;
144-
145-
public SimpleProbabilisticThreatAwareSystem(final String systemId, final List<ProbableThreat> threats) {
146-
this.systemId = systemId;
147-
this.threats = ImmutableList.copyOf(threats);
148-
}
149-
150-
@Override
151-
public String systemId() {
152-
return systemId;
153-
}
154-
155-
@Override
156-
public List<? extends ProbableThreat> threats() {
157-
return threats;
158-
}
159-
160-
@Override
161-
public Filterer<? extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> filtered() {
162-
return this::filteredGroup;
163-
}
164-
165-
private ProbabilisticThreatAwareSystem filteredGroup(final Predicate<? super ProbableThreat> predicate) {
166-
return new SimpleProbabilisticThreatAwareSystem(this.systemId, filteredItems(predicate));
167-
}
168-
169-
private List<ProbableThreat> filteredItems(final Predicate<? super ProbableThreat> predicate) {
170-
return this.threats.stream()
171-
.filter(predicate)
172-
.collect(Collectors.toList());
173-
}
133+
private final String systemId;
134+
private final ImmutableList<ProbableThreat> threats;
135+
136+
public SimpleProbabilisticThreatAwareSystem(final String systemId, final List<ProbableThreat> threats) {
137+
this.systemId = systemId;
138+
this.threats = ImmutableList.copyOf(threats);
139+
}
140+
141+
@Override
142+
public String systemId() {
143+
return systemId;
144+
}
145+
146+
@Override
147+
public List<? extends ProbableThreat> threats() {
148+
return threats;
149+
}
150+
151+
@Override
152+
public Filterer<? extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> filtered() {
153+
return this::filteredGroup;
154+
}
155+
156+
private ProbabilisticThreatAwareSystem filteredGroup(final Predicate<? super ProbableThreat> predicate) {
157+
return new SimpleProbabilisticThreatAwareSystem(this.systemId, filteredItems(predicate));
158+
}
159+
160+
private List<ProbableThreat> filteredItems(final Predicate<? super ProbableThreat> predicate) {
161+
return this.threats.stream()
162+
.filter(predicate)
163+
.collect(Collectors.toList());
164+
}
174165
}
175166
```
176167

177168
Now if we want filter `ThreatAwareSystem` by threat type we can do:
178169

179170
```java
180-
Threat rootkit = new SimpleThreat(ThreatType.ROOTKIT, 1, "Simple-Rootkit");
181-
Threat trojan = new SimpleThreat(ThreatType.TROJAN, 2, "Simple-Trojan");
182-
List<Threat> threats = List.of(rootkit, trojan);
171+
Threat rootkit=new SimpleThreat(ThreatType.ROOTKIT, 1, "Simple-Rootkit");
172+
Threat trojan=new SimpleThreat(ThreatType.TROJAN, 2, "Simple-Trojan");
173+
List<Threat> threats=List.of(rootkit, trojan);
183174

184-
ThreatAwareSystem threatAwareSystem = new SimpleThreatAwareSystem("System-1", threats);
175+
ThreatAwareSystem threatAwareSystem=new SimpleThreatAwareSystem("System-1", threats);
185176

186-
ThreatAwareSystem rootkitThreatAwareSystem = threatAwareSystem.filtered()
187-
.by(threat -> threat.type() == ThreatType.ROOTKIT);
177+
ThreatAwareSystem rootkitThreatAwareSystem=threatAwareSystem.filtered().by(threat -> threat.type() == ThreatType.ROOTKIT);
188178
```
189179

190180
Or if we want to filter `ProbabilisticThreatAwareSystem`:
191181

192182
```java
193-
ProbableThreat malwareTroyan = new SimpleProbableThreat("Troyan-ArcBomb", 1, ThreatType.TROJAN, 0.99);
183+
ProbableThreat malwareTroyan=new SimpleProbableThreat("Troyan-ArcBomb", 1, ThreatType.TROJAN, 0.99);
194184
ProbableThreat rootkit = new SimpleProbableThreat("Rootkit-System", 2, ThreatType.ROOTKIT, 0.8);
195185
List<ProbableThreat> probableThreats = List.of(malwareTroyan, rootkit);
196186

197-
ProbabilisticThreatAwareSystem simpleProbabilisticThreatAwareSystem =new SimpleProbabilisticThreatAwareSystem("System-1", probableThreats);
187+
ProbabilisticThreatAwareSystem simpleProbabilisticThreatAwareSystem = new SimpleProbabilisticThreatAwareSystem("System-1", probableThreats);
198188

199-
ProbabilisticThreatAwareSystem filtered = simpleProbabilisticThreatAwareSystem.filtered()
200-
.by(probableThreat -> Double.compare(probableThreat.probability(), 0.99) == 0);
189+
ProbabilisticThreatAwareSystem filtered = simpleProbabilisticThreatAwareSystem.filtered().by(probableThreat -> Double.compare(probableThreat.probability(), 0.99) == 0);
201190
```
202191

203192
## Class diagram
@@ -206,30 +195,38 @@ ProbabilisticThreatAwareSystem filtered = simpleProbabilisticThreatAwareSystem.f
206195

207196
## Applicability
208197

209-
Pattern can be used when working with container-like objects that use subtyping, instead of
210-
parametrizing (generics) for extensible class structure. It enables you to easily extend filtering
211-
ability of container-like objects as business requirements change.
198+
This pattern is useful in scenarios where data needs to be processed in discrete steps, and each step's output is the input for the next step. Common in stream processing, audio/video processing pipelines, or any data processing applications requiring staged transformations.
212199

213200
## Tutorials
214201

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

218-
## Known uses
205+
## Known Uses
219206

220-
One of the uses is present on the blog presented in
221-
[this](https://www.javacodegeeks.com/2019/02/filterer-pattern-10-steps.html) link. It presents how
222-
to use `Filterer` pattern to create text issue analyzer with support for test cases used for unit
223-
testing.
207+
* Stream processing libraries in Java, such as Apache Kafka Streams, utilize this pattern to build complex data processing pipelines.
208+
* Image processing software often uses filters to apply effects or transformations to images sequentially.
224209

225210
## Consequences
226211

227-
Pros:
228-
* 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.
212+
Benefits:
213+
214+
* Increases flexibility by allowing different filters to be added or reorganized without affecting other parts of the system.
215+
* Enhances testability, as filters can be tested independently.
216+
* Promotes loose coupling between the stages of data processing.
217+
218+
## Trade-offs:
219+
220+
* Potential performance overhead from continuous data passing between filters.
221+
* Complexity can increase with the number of filters, potentially affecting maintainability.
222+
223+
## Related Patterns
229224

230-
Cons:
231-
* Covariant return types mixed with generics can be sometimes tricky
225+
* Chain of Responsibility: Filters can be seen as a specialized form of the Chain of Responsibility, where each filter decides if and how to process the input data and whether to pass it along the chain.
226+
* Decorator: Similar to Decorator in that both modify behavior dynamically; however, filters focus more on data transformation than on adding responsibilities.
232227

233228
## Credits
234229

235-
* Author of the pattern : [Tomasz Linkowski](https://tlinkowski.pl/)
230+
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3W8sn2W)
231+
* [Kafka: The Definitive Guide: Real-Time Data and Stream Processing at Scale](https://amzn.to/49N3nRU)
232+
* [Java Performance: The Definitive Guide](https://amzn.to/3vRW3qj)

filterer/src/main/java/com/iluwatar/filterer/App.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
* objects are systems that are aware of threats that they can be vulnerable to. We would like
4343
* to have a way to create copy of different system objects but with filtered threats.
4444
* The thing is to keep it simple if we add new subtype of {@link Threat}
45-
* (for example {@link ProbableThreat}) - we still need to be able to filter by it's properties.
45+
* (for example {@link ProbableThreat}) - we still need to be able to filter by its properties.
4646
*/
4747
@Slf4j
4848
public class App {

filterer/src/main/java/com/iluwatar/filterer/threat/ProbabilisticThreatAwareSystem.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import java.util.List;
2929

3030
/**
31-
* Represents system that is aware of it's threats with given probability of their occurrence.
31+
* Represents system that is aware of its threats with given probability of their occurrence.
3232
*/
3333
public interface ProbabilisticThreatAwareSystem extends ThreatAwareSystem {
3434

filterer/src/main/java/com/iluwatar/filterer/threat/SimpleProbabilisticThreatAwareSystem.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import com.iluwatar.filterer.domain.Filterer;
2828
import java.util.List;
2929
import java.util.function.Predicate;
30-
import java.util.stream.Collectors;
3130
import lombok.EqualsAndHashCode;
3231
import lombok.RequiredArgsConstructor;
3332
import lombok.ToString;

filterer/src/main/java/com/iluwatar/filterer/threat/SimpleThreatAwareSystem.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import java.util.ArrayList;
2929
import java.util.List;
3030
import java.util.function.Predicate;
31-
import java.util.stream.Collectors;
3231
import lombok.EqualsAndHashCode;
3332
import lombok.RequiredArgsConstructor;
3433
import lombok.ToString;

0 commit comments

Comments
 (0)