Skip to content

Commit

Permalink
Added fixes after review. Changed example pattern application to thre…
Browse files Browse the repository at this point in the history
…at detection domain
  • Loading branch information
mkrzywanski committed Aug 22, 2020
1 parent 905b5dc commit 61a819a
Show file tree
Hide file tree
Showing 18 changed files with 568 additions and 332 deletions.
170 changes: 164 additions & 6 deletions filterer/README.MD
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
--- # this is so called 'Yaml Front Matter', read up on it here: http://jekyllrb.com/docs/frontmatter/
layout: pattern
title: Filterer Pattern
title: Filterer
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
Expand All @@ -11,15 +11,173 @@ tags:
---

## Name / classification
Filterer Pattern
Filterer

## 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
The container-like object needs to have a method that returns an instance of `Filterer`. This helper interface gives
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
ability to covariantly specify a lower bound of contravariant `Predicate` in the subinterfaces of interfaces representing the container-like objects.

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);
```
## Class diagram
![Filterer](./etc/filterer.png "Filterer")

Expand All @@ -34,11 +192,11 @@ It enables you to easily extend filtering ability of container-like objects as b
## Known uses
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.

## Consequences (the good and the bad, add criticism here)
Good :
## Consequences
Pros :
* 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.

Bad :
Cons :
* covariant return types mixed with generics can be sometimes tricky

## Credits
Expand Down
Binary file modified filterer/etc/filterer.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 61a819a

Please sign in to comment.