2016-02-01 45 views
6

Quindi ero curioso di sapere come il seguente codice sarebbe stato riscritto in modo più efficiente con l'API java8 streams.Consuming Rest Service the functional way

public static List<FlightInfo> getResults(String origin,List<String> destinations) { 

    final String uri = "https://api.searchflight.com/; 
    List<FlightInfo> results = new LinkedList<FlightInfo>(); 

    for(String destination:destinations) { 


      RestTemplate restTemplate = new RestTemplate(); 

      String params = getParams(origin,destination); 
      FlightInfo result = restTemplate.postForObject(uri+params,FlightInfo.class); 

      results.add(result); 
    } 

    return results; 

} 

Dopo questo metodo è fatto fare quello che sta facendo il suo e ricevo l'elenco degli oggetti FLightInfo, io sono la conversione in un flusso e cercherò di fare varie trasformazioni su di esso (gruppo da, ecc). Ora è abbastanza evidente che si tratta di un'operazione a lungo termine. Inoltre, in realtà combina più chiamate di resto al servizio web, quindi ho già la maggior parte dei dati ottenuti nel momento in cui effettuo l'ultima chiamata, ma non inizierei ad elaborarla prima che l'intero metodo ritorni.

C'è un modo per fare tutto ciò in modo un po 'più reattivo? Potrei restituire uno stream immediatamente e fare in modo che le operazioni su quel flusso elaborino i dati mentre scende la pipe o questo è un po 'troppo chiedere? Come sarebbe fatto in Java 8. Che

risposta

5

Beh tutto dipende da quando hai bisogno del risultato. Se desideri che sia sequenziale, questo sotto è ancora un modo decente come il suo pigro. Ma bollirebbe a un'operazione terminale (diciamo durante il collect).

public static Stream<FlightInfo> getResults(String origin,List<String> destinations) { 
    final String uri = "https://api.searchflight.com/"; 
    return destinations.stream().map(destination -> { 
     RestTemplate restTemplate = new RestTemplate(); 
     String params = getParams(origin,destination); 
     FlightInfo result = restTemplate.postForObject(uri+params,FlightInfo.class); 
     return result; 
    })  
} 

Oppure lo farei con destinations.stream().parallel() se possibile. Questo è un risultato ragionevole nella maggior parte delle circostanze. Ma ancora non inizierà l'elaborazione in parallelo fino al momento in cui si chiama un'operazione terminale per esso. Il che ha assolutamente senso.

Ma mi sembra che tu desideri un tipo di produttore-consumatore. Per cui:

public static CompletableFuture<List<FlightInfo>> getResults(String origin,List<String> destinations) { 
    final String uri = "https://api.searchflight.com/"; 
    List<CompletableFuture<FlightInfo>> collect = destinations 
      .stream() 
      .map(destination -> CompletableFuture.supplyAsync(() -> { 
       RestTemplate restTemplate = new RestTemplate(); 
       String params = getParams(origin,destination); 
       FlightInfo result = restTemplate.postForObject(uri+params,FlightInfo.class); 
       return result;    
      })).collect(Collectors.toList()); 
    return sequence(collect);  //line-1 
} 

public static <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> com) { 
    return CompletableFuture.allOf(com.toArray(new CompletableFuture[com.size()])) 
      .thenApply(v -> com.stream() 
          .map(CompletableFuture::join) 
          .collect(Collectors.toList()) 
      ); 
} 

Per semplicità, a line-1 si può semplicemente tornare collect invece di sequence(collect). È quindi possibile scorrere l'elenco per recuperare ogni valore.

Ma con sequence, si ha un singolo oggetto CompletableFuture di cui preoccuparsi, che è quindi possibile verificare i valori in una volta, se completato.

+0

Quindi è corretto dire che l'approccio futuro completo si sovrapporrà all'elaborazione con il recupero dei risultati. A quanto sembra, questo è davvero il consumatore produttore mappato per utilizzare i flussi. In tal caso, cosa diresti sono i benefici di questo particolare approccio rispetto all'implementazione del produttore-consumatore (a parte la brevità e la mancanza dello standard). A proposito, sono davvero grato per la soluzione fornita e penso sia grandioso. Sto solo cercando di capire meglio e questo è il motivo per cui faccio le domande. Grazie ancora – Zahari

+1

Semplicemente quello sopra CompletableFuture fornisce una bella astrazione per il consumatore produttore. Bene, se sembra brutto, il che è vero, ma se il verificarsi del pattern sopra è elevato rispetto al codice base, consiglio di consultare http://github.com/ReactiveX/RxJava/wiki. Fornisce belle astrazioni come Observables (puoi pensare come un flusso) a cui puoi avere risultati una volta disponibili. Poiché le astrazioni sono solide, hai una salda presa di espressività – Jatin