El uso de valores de encadenado previamente thenCompose lambdas en Java 8

Andrew Cheong:

El estilo de programación Java 8 preferido por mis colegas está encadenando llamadas asíncronas todo el camino a través, por ejemplo,

CompletionStage<E> someMethod() {
    return doSomething().thenCompose(a -> {
      // ...
      return b;
    }).thenCompose(b -> {
      // ...
      return c;
    }).thenCompose(c -> {
      // ...
      return d;
    }).thenApply(d -> {
      // ...
      return e;
    });
}

Tengo algo así como el anterior, pero con un problema añadido: Necesito recordar valores recuperados dentro de algunos de los lambdas, en lambdas posteriores. Por ejemplo,

CompletionStage<E> someMethod() {
    return doSomething().thenCompose(a -> {
      // ...
      Foo foo = fooDAO.getFoos(a);
      // ...
      return b;
    }).thenCompose(b -> {
      // ...
      return c;
    }).thenCompose(c -> {
      // ...
      Bar bar = barDAO.getBars(foo);
      // ...
      return d;
    }).thenApply(d -> {
      // ...
      return someResult(d, bar);
    });
}

Cuando Declaro Foo foo;y Bar bar;en el ámbito exterior, recibo errores acerca de ellos no ser definitiva o con eficacia final. Y leí sobre el uso de un envoltorio para que sean efectivamente final, pero parece más bien hacky a mí para hacer esto (que no entiendo por qué eso está permitido ...)

He leído que Java 8 no añadir soporte para tuplas (aunque se considera BiValque tal vez podría haber usado para pasar a BiFunctionlambdas). Así que he intentado utilizando pares, por ejemplo,

    return doSomething().thenCompose(a -> {
      // ...
      Foo foo = fooDAO.getFoos(a);
      // ...
      return new Pair<>(foo, b);
    }).thenCompose(fooAndB -> {

y donde quiera que necesitaba para recordar foo,

      Foo foo = fooAndB.getKey();

Pero esto se siente tan mal semánticamente. Además, no funciona! No sé por qué, porque pensé que el alcance de un parámetro lambda es la misma que fuera de su alcance, y así todos los parámetros lambda serían accesibles desde el interior de lambdas encadenados más tarde.

Lo que es realmente el alcance de los parámetros lambda, y es que hay un idiomática o por lo menos forma semánticamente unoffensive de hacer lo que me gustaría hacer mientras se mantiene el encadenamiento?

Respuestas basadas en romper la cadena están muy bien, ya que pueden ser útiles para los espectadores futuras, pero en mi caso, las desviaciones del estilo dominante en este repo pueden resultar en dibujadas a cabo conversaciones de relaciones públicas y la aprobación tardía, por lo que me gustaría una solución que preserve el encadenamiento. O bien, una explicación o demostración de cómo loco sería para tratar de mantener el encadenamiento. ¡Gracias!

Didier L:

Ya que mencionas el estilo de codificación preferido por sus colegas , es probable que ya conoce la alternativa de utilizar las llamadas anidadas en su lugar:

CompletionStage<E> someMethod() {
    return doSomething().thenCompose(a -> {
        // ...
        Foo foo = fooDAO.getFoos(a);
        // ...
        CompletableFuture<B> resultB = ...;
        return resultB.thenCompose(b -> {
            // ...
            CompletableFuture<C> resultC = ...;
            return resultC;
        }).thenCompose(c -> {
            // ...
            Bar bar = barDAO.getBars(foo);
            // ...
            CompletableFuture<D> resultD = ...;
            return resultD.thenApply(d -> {
                // ...
                return someResult(d, bar);
            });
        });
    });
}

Esto soluciona su problema de inmediato, a costa de código un poco menos legible. Sin embargo, este problema puede ser fácilmente fijado por la extracción de algunos métodos de su código:

CompletionStage<E> someMethod() {
    return doSomething()
            .thenCompose(this::processA);
}

private CompletionStage<E> processA(final A a) {
    // ...
    Foo foo = fooDAO.getFoos(a);
    // ...
    final CompletableFuture<B> result = ...;
    return result
            .thenCompose(this::processB)
            .thenCompose(c -> processCAndFoo(c, foo));
}

private CompletionStage<C> processB(B b) {
    // ...
    return ...;
}

private CompletionStage<E> processCAndFoo(final C c, final Foo foo) {
    // ...
    Bar bar = barDAO.getBars(foo);
    // ...
    final CompletableFuture<D> result = ...;
    return result
            .thenApply(d -> someResult(d, bar));
}

De esta manera, se evita lambdas anidados (y respetar el código de estilo preferido de sus colegas), pero también ganar en legibilidad y la capacidad de prueba ya que ahora tiene varios métodos pequeñas que son más fáciles de entender y de prueba de la unidad.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=171312&siteId=1
Recomendado
Clasificación