在非反应性世界中,以下代码段没什么特别的:
interface Enhancer {
Result enhance(Result result);
}
Result result = Result.empty();
result = fooEnhancer.enhance(result);
result = barEnhancer.enhance(result);
result = bazEnhancer.enhance(result);
有三种不同的Enhancer实现,它们采用Result实例,对其进行增强并返回增强的结果。让我们假设增强程序调用的顺序很重要。
现在,如果将这些方法替换为返回Flux<Result>
的反应性变量,该怎么办?由于这些方法取决于先前方法的结果,因此我们不能在此处使用combineLatest
。
可能的解决方案可能是:
Flux.just(Result.empty())
.switchMap(result -> first(result)
.switchMap(result -> second(result)
.switchMap(result -> third(result))))
.subscribe(result -> doSomethingWith(result));
请注意,switchMap
调用是嵌套的。由于我们只对最终结果感兴趣,因此,只要先前的通量中发出新的事件,我们就让switchMap
切换到下一个通量。
现在让我们尝试使用动态数量的通量来实现。非反应性的(无助焊剂),这再次没有什么特别的:
List<Enhancer> enhancers = <ordered list of different Enhancer impls>;
Result result = Result.empty();
for (Enhancer enhancer : enhancers) {
result = enhancer.enhance(result);
}
但是我该如何用三个通量来概括上述反应式示例以处理任意数量的通量?
答案 0 :(得分:0)
我找到了使用递归的解决方案:
@FunctionalInterface
interface FluxProvider {
Flux<Result> get(Result result);
}
// recursive method creating the final Flux
private Flux<Result> cascadingSwitchMap(Result input, List<FluxProvider> fluxProviders, int idx) {
if (idx < fluxProviders.size()) {
return fluxProviders.get(idx).get(input).switchMap(result -> cascadingSwitchMap(result, fluxProviders, idx + 1));
}
return Flux.just(input);
}
// code using the recursive method
List<FluxProvider> fluxProviders = new ArrayList<>();
fluxProviders.add(fooEnhancer::enhance);
fluxProviders.add(barEnhancer::enhance);
fluxProviders.add(bazEnhancer::enhance);
cascadingSwitchMap(Result.empty(), fluxProviders, 0)
.subscribe(result -> doSomethingWith(result));
但是使用项目反应器的操作者/功能也许会有更优雅的解决方案。有人知道这样的功能吗?实际上,这一要求似乎并不罕见,不是吗?
答案 1 :(得分:0)
switchMap
在这里感到不合适。如果您在List<Enhancer>
管道被声明时拥有Flux
,为什么不采用与命令式相似的逻辑:
List<Enhancer> enhancers = <ordered list of different Enhancer impls>;
Mono<Result> resultMono = Mono.just(Result.empty)
for (Enhancer enhancer : enhancers) {
resultMono = resultMono.map(enhancer::enhance); //previousValue -> enhancer.enhance(previousValue)
}
return resultMono;
甚至可以在订阅时稍后执行,通过将上面的整个代码包装在Mono.defer(() -> {...})
块中,以实现增强器的更动态解析。