Translate complex conditional logic inside a loop into threads and lambdas

I am looking for a clean way to translate complex logical conditions with if and else that lead to different actions in lambdas and threads.

Suppose I have this code:

 List<OuterData> result = new LinkedList<>(); for (Outer outer : getOutersFromSomewhere()) { OuterData outerData = new OuterData(); if (outer.isImportant()) { doImportantAction(outer, outerData); } else if (outer.isTrivial()) { doTrivialAction(outer, outerData); } else { doDefaultAction(outer, outerData); } for (Inner inner : outer.getInners()) { if (inner.mustBeIncluded()) { InnerData innerData = new InnerData(); if (inner.meetsCondition1()) { doAction1(inner, innerData, outer, outerData); } else if (inner.meetsCondition2()) { doAction2(inner, innerData, outer, outerData); } else { doDefaultAction(inner, innerData, outer, outerData); } outerData.add(innerData); } } result.add(outerData); } return result; 

This is simplified from the real code that I have. I know that it can be optimized and reorganized, i.e. I can move the internal for to the private method. I would like to know how to translate the parts of if , else if and else into streams and lambdas.

I know how to translate the skeleton of this example. I would use List.stream() , Stream.map() , Stream.filter() , Stream.collect() and Stream.peek() . My problem is only with conditional branches. How can I make this translation?

+5
source share
1 answer

The first obvious way is to stream your items, filter them according to the necessary criteria, and then apply the action to each remaining item. It also makes the code much cleaner:

 List<Outer> outers = getOutersFromSomewhere(); outers.stream().filter(Outer::isImportant) .forEach(outer -> doImportantAction(outer, outerDate)); outers.stream().filter(Outer::isTrivial) .forEach(outer -> doTrivialAction(outer, outerDate)); // default action analog 

Caution: This only works if important, trivial, and standard elements form a partition. Otherwise, this is not equivalent to your if-else structure. But maybe it's all the same ...

The main problem with this approach: it is not a very good OOP. You request objects to make a decision. But OOP should β€œtell, don't ask” as much as possible.

So, another solution is to provide a consumption method in your Outer class:

 public class Outer { ... public void act(OuterData data, Consumer<Outer> importantAction, Consumer<Outer> trivialAction, Consumer<Outer> defaultAction) { if (isImportant()) importantAction.accept(this, data); else if (isTrivial()) trivialAction.accept(this, data); else defaultAction.accept(this, data); } } 

Now you call it that simple:

 List<Outer> outers = getOutersFromSomewhere(); outers.forEach(outer -> outer.act(...)); // place consumers here (lambdas) 

This has a clear advantage: if you ever need to add a function to the Outer class - say, isComplex() - you only need to change the internals of this single class (and possibly allow the compiler to fail in other parts). Or maye you can add this function in a backward compatible way.

The same rules can apply to the Inner class and iteration.

+5
source

Source: https://habr.com/ru/post/1215763/


All Articles