As per a previous question here and answers I got, I used it to get a new implementation. I found that I have to make two implementations match once "to ignore other consumers after matching once" and match all "to execute all consumers its filter matching the input", and made a common interface to abstract usage of two implementations:
Common interface
public interface Router<T> {
Router<T> chain(Predicate<T> p, Consumer<T> c);
Consumer<T> toConsumer();
}
Match All:
public interface RouterMatchAll<T> extends Consumer<T>, Router<T> {
static <T> RouterMatchAll<T> of(Predicate<T> p1, Consumer<T> c1) {
BiFunction<Predicate<T>, Consumer<T>, RouterMatchAll<T>> preFun = (p, c) -> i -> Optional
.ofNullable(i).filter(p).ifPresent(c);
return preFun.apply(p1, c1);
}
@Override
public default RouterMatchAll<T> chain(Predicate<T> p, Consumer<T> c) {
return this.andThen(RouterMatchAll.of(p, c))::accept;
}
@Override
public default Consumer<T> toConsumer() {
return this;
}
}
Match Once:
public interface RouterMatchOnce<T> extends Function<Optional<T>, Optional<T>>,
Router<T> {
static <T> RouterMatchOnce<T> of(Predicate<T> p1, Consumer<T> c1) {
BiFunction<Predicate<T>, Consumer<T>, RouterMatchOnce<T>> preFun = (p,
c) -> o -> {
if (o.filter(p).isPresent()) {
c.accept(o.get());
return Optional.empty();
}
return o;
};
return preFun.apply(p1, c1);
}
@Override
public default RouterMatchOnce<T> chain(Predicate<T> p, Consumer<T> c) {
return this.andThen(RouterMatchOnce.of(p, c))::apply;
}
@Override
public default Consumer<T> toConsumer() {
return i -> this.apply(Optional.ofNullable(i));
}
}
For MatchAll
, I used Consumer
to use its andThen
, which consumes the same input. For MatchOnce
, I had to use Function<Optional,Optional>
and its andThen
, use the output of first function to be used in next function, and I needed to change the data to Optional.empty()
after consuming once to not consume it then.
Concerns I want your feedback about:
toConsumer()
inRouterMatchAll
is just return this and I don't like it but couldn't find another option for abstraction.- Names are not nice as it is not a router any more. It has to be something like filter, interceptor, or
ConditionalConsumer
. Please suggest a name for these interfaces, chain, andtoConsumer
methods.
Usage of the above:
Predicate<Integer> p1 = (Integer i) -> {
System.out.println("call p1");
return i < 5;
};
Predicate<Integer> p2 = (Integer i) -> {
System.out.println("call p2");
return i > 5;
};
Consumer<Integer> c1 = i -> System.out.println("less");
Consumer<Integer> c2 = i -> System.out.println("more");
Router<Integer> routerMatchAll = RouterMatchAll.of(p1, c1)
.chain(p2, c2);
IntStream.range(0, 10).boxed().forEach(routerMatchAll.toConsumer());
System.out.println(">>>>>>>>>>>>>>>>>>>>>>");
Router<Integer> routerMatchOnce = RouterMatchOnce.of(p1, c1).chain(p2,
c2);
IntStream.range(0, 10).boxed().forEach(routerMatchOnce.toConsumer());
Output:
call p1 less call p2 call p1 less call p2 call p1 less call p2 call p1 less call p2 call p1 less call p2 call p1 call p2 call p1 call p2 more call p1 call p2 more call p1 call p2 more call p1 call p2 more >>>>>>>>>>>>>>>>>>>>>> call p1 less call p1 less call p1 less call p1 less call p1 less call p1 call p2 call p1 call p2 more call p1 call p2 more call p1 call p2 more call p1 call p2 more
Collections.partitioningBy()
and then callforEach()
on the resultingMap
's values... \$\endgroup\$Optional
-based suggestion regardingnull
-handling. If that affects your implementation, then you should switch to your originalif
-statement too. :) \$\endgroup\$