Java Streams filters with side effects
Once you get used to stream programming and the pattern of create, select, manipulate and collect your code will never look the same
Putting side effects to good (?) use
The pure teachings tell us, filters should select objects for processing and not have any side effects or do processing on their own. But ignoring the teachings could produce clean code (I probably will roast in debug hell for this). Let's look at an example:
final Collection<MyNotification> notifications = getNotifications();
final Iterator<MyNotification> iter = notifications.iterator();
while(iter.hasNext()) {
MyNotification n = iter.next();
if (n.priority == Priority.high) {
sendHighPriority(n);
} else if (n.groupNotification) {
sendGroupNotification(n);
} else if (n.special && !n.delay > 30) {
sendSpecial(n);
} else if (!n.special) {
sendStandard(n);
} else {
reportWrongNotification(n);
}
}
This gets messy very fast and all selection logic is confined to the if conditions in one function (which initially looks like a good idea). How about rewriting the code Stream style? It will be more boiler plate, but better segregation:
final Stream<MyNotification> notifications = getNotifications();
notifications
.filter(this::highPriority)
.filter(this::groupSend)
.filter(this::specialNoDelay)
.filter(this::standard)
.forEach(this::reportWrongNotification);
The filter functions would look like this:
boolean highPriority(final MyNotification n) {
if (n.priority == Priority.high) {
sendHighPriority(n);
return false; // No further processing required
}
return true; // Furhter processing required
}
boolean groupSend(final MyNotification n) {
if (n.groupNotification) {
sendGroupNotification(n);
return false; // No further processing required
}
return true; // Furhter processing required
}
You get the idea. With proper JavaDoc method headers, this code looks more maintainable.
We can push this a little further (as explored on Stackoverflow). Imagin the number of process steps might vary and you don't want to update that code for every variation. You could do something like this:
final Stream<MyNotification> notifications = getNotifications();
final Stream<Predicate<MyNotifications>> filters = getFilters();
notifications
.filter(filters.reduce(f -> true, Predicate::and))
.forEach(this::reportWrongNotification);
As usual YMMV
Posted by Stephan H Wissel on 22 October 2021 | Comments (1) | categories: Java