2016-02-01 4 views
6

So war ich neugierig, wie würde der folgende Code effizienter mit der Java-Stream-API neu geschrieben werden.Consuming Rest Service der funktionale Weg

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; 

} 

Nach diesem Verfahren erfolgt das tun, was seine tut, und ich erhalte die Liste der FLightInfo Objekte, ich bin dessen Umwandlung in einen Strom und wird (durch etc Gruppe) verschiedene Transformationen es tun. Jetzt ist es ziemlich offensichtlich, dass dies eine langwierige Operation ist. Außerdem kombiniert es tatsächlich mehrere Ruheaufrufe mit dem Web-Service, so dass ich die meisten der Daten schon beim letzten Aufruf erhalten habe, aber ich würde nicht mit der Verarbeitung beginnen, bevor die ganze Methode zurückkommt.

Gibt es eine Möglichkeit, all das ein bisschen reaktiver zu machen? Könnte ich sofort einen Stream zurückliefern und die Operationen in diesem Stream Daten verarbeiten lassen, während es in der Pipe ankommt? Das ist ein bisschen zu viel verlangt? Wie würde das in Java 8 getan werden. Das

Antwort

5

Nun, alles hängt davon ab, wann Sie das Ergebnis benötigen. Wenn Sie möchten, dass es sequenziell ist, ist dies unten ein anständiger Weg, wie es faul ist. Aber es würde bei einem Terminalbetrieb kochen (sagen wir während 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; 
    })  
} 

Oder ich würde es mit destinations.stream().parallel() tun, wenn ich kann. Dies ist in den meisten Fällen ein vernünftiges Ergebnis. Aber es wird immer noch nicht parallel zu dem Zeitpunkt, an dem Sie eine Terminaloperation dafür aufrufen, mit der Verarbeitung beginnen. Was absolut Sinn macht.

Aber es sieht für mich aus, dass Sie für einen Hersteller-Verbraucher-Typ einer Sache wünschen. Für die gilt:

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()) 
      ); 
} 

für Einfachheit, bei line-1 können Sie einfach collect zurückkehren statt sequence(collect). Sie können dann über die Liste iterieren, um jeden Wert abzurufen.

Aber mit sequence haben Sie ein einzelnes CompleableFuture-Objekt, um das Sie sich kümmern müssen. Sie können dann nach Werten suchen, wenn Sie fertig sind.

+0

Also ist es richtig zu sagen, dass der vollständige zukünftige Ansatz die Verarbeitung mit dem Abrufen der Ergebnisse überlappen soll. Nach dem Aussehen ist dies wirklich der Producer Consumer zugeordnet, um Streams zu verwenden. Was würden Sie in diesem Fall als Vorteile dieses speziellen Ansatzes gegenüber der Implementierung von Produzenten - Konsumenten bezeichnen (abgesehen von der Kürze und dem Mangel an Standardprodukten)? Ich bin übrigens sehr dankbar für die gelieferte Lösung und finde es großartig. Ich versuche nur, besser zu verstehen, und deshalb fragt man die Fragen. Nochmals vielen Dank – Zahari

+1

Einfach das oben CompleableFuture bietet eine nette Abstraktion für Produzenten Verbraucher. Nun, wenn es hässlich aussieht, was es aber ist, wenn das obige Muster über der Code-Basis hoch ist, dann empfehle ich, http://github.com/ReactiveX/RxJava/wiki zu betrachten. Es bietet nette Abstraktionen als Observables (man kann sich das wie einen Stream vorstellen), zu denen man einmal Ergebnisse erhalten kann. Weil Abstraktionen gesund sind, haben Sie einen festen Ausdruck – Jatin