Кортежи — это простые структуры данных, которые содержат фиксированный набор элементов, каждый из которых имеет свой тип данных.
Project Reactor предоставляет структуру данных Tuple, которая может содержать около 8 различных типов.
Кортежи очень полезны, однако одна из проблем при использовании кортежей заключается в том, что трудно разобрать, что они держат, не разрушая их в каждом месте, где они используются.
проблема
Рассмотрим простой оператор, который объединяет строку и целое число следующим образом:
1
|
Mono<Tuple2<String, Integer>> tup = Mono.zip(Mono.just( "a" ), Mono.just( 2 )) |
Результатом является кортеж, содержащий 2 элемента.
Теперь проблема, которая у меня есть, заключается в следующем. Я предпочитаю, чтобы кортеж разыменовывался в его составные элементы, прежде чем что-либо делать с ним.
Скажем, с предыдущим кортежем я хочу сгенерировать столько строк (первый элемент кортежа), сколько count (второй элемент кортежа) выражается следующим образом:
1
2
3
4
|
Mono.zip(Mono.just( "a" ), Mono.just( 2 )) .flatMapMany(tup -> { return Flux.range( 1 , tup.getT2()).map(i -> tup.getT1() + i); }) |
Проблема здесь заключается в том, что при обращении к «tup.getT1 ()» и «tup.getT2 ()» не совсем понятно, что держит кортеж. Хотя более многословно я бы предпочел сделать что-то вроде этого:
1
2
3
4
5
6
7
|
Mono.zip(Mono.just( "a" ), Mono.just( 2 )) .flatMapMany(tup -> { String s = tup.getT1(); int count = tup.getT2(); return Flux.range( 1 , count).map(i -> s + i); }); |
Решение
Подход деструктуризации в явные переменные со значимым именем работает, но он немного многословен. Гораздо лучший подход обеспечивается с помощью набора служебных функций, которые поставляются с проектным реактором.
TupleUtils
Я чувствую, что это лучше всего объяснить, используя пример, с TupleUtils предыдущая деструктуризация выглядит следующим образом:
1
2
3
|
Mono.zip(Mono.just( "a" ), Mono.just( 2 )) .flatMapMany(TupleUtils.function((s, count) -> Flux.range( 1 , count).map(i -> s + i))) |
Это выглядит гораздо более кратким, чем явная деструктуризация. Хотя есть немного сообразительности, к которой нужно привыкнуть:
Подпись flatMapMany является —
1
|
public final <R> Flux<R> flatMapMany(Function<? super T,? extends Publisher<? extends R>> mapper) |
TupleUtils предоставляет другое косвенное обращение, которое возвращает требуемую выше функцию через другую функцию:
1
2
3
|
public static <T1, T2, R> Function<Tuple2<T1, T2>, R> function(BiFunction<T1, T2, R> function) { return tuple -> function.apply(tuple.getT1(), tuple.getT2()); } |
Если вы используете Kotlin, возможен более простой подход. Это основано на концепции «уничтожающих деклараций» . Проектный реактор предоставляет набор вспомогательных утилит Kotlin, использующих дополнительную зависимость gradle:
1
|
implementation( "io.projectreactor.kotlin:reactor-kotlin-extensions" ) |
С этой зависимостью эквивалентный код Kotlin выглядит так:
1
2
3
4
|
Mono.zip(Mono.just( "a" ), Mono.just( 2 )) .flatMapMany { (s: String, count: Int) -> Flux.range( 1 , count).map { i: Int -> s + i } } |
Посмотрите, как кортеж был деструктурирован непосредственно в переменные. Это выглядит так изолированно:
1
|
val (s: String, count: Int) = tup |
Вывод
Важно деструктурировать кортеж в более значимые переменные, чтобы улучшить читабельность кода, а полезные функции, предоставляемые TupleUtils, а также расширениями Kotlin, помогают сохранить код кратким, но читабельным.
Примеры Java здесь и Kotlin образцы здесь
Смотрите оригинальную статью здесь: Проектный реактор — деструктурирование кортежа Мнения, высказанные участниками Java Code Geeks, являются их собственными. |