2016-04-02 11 views
2

Ich erstelle ein Demonstrationsbeispiel für die Verkettung CompleteableFuture Operationen und ich denke, ich bin in der Nähe, aber es gibt etwas, das ich vermisse. Alles kompiliert mit Ausnahme des letzten „Build Kuchen parallel“ -Klausel in main():Java 8 thenApply() und thenAccept()

import java.util.*; 
import java.util.concurrent.*; 
import java.util.function.*; 
import java.util.stream.*; 
import java.time.*; 

// Use decorator pattern to build up the cake: 

interface Cake_ { 
    String describe(); 
} 

class Cake implements Cake_ { 
    private int id; 
    public Cake(int id) { this.id = id; } 
    @Override 
    public String describe() { 
    return "Cake " + id; 
    } 
} 

abstract class Decorator implements Cake_ { 
    protected Cake_ cake; 
    public Decorator(Cake_ cake) { 
    this.cake = cake; 
    } 
    @Override 
    public String describe() { 
    return cake.describe(); 
    } 
    @Override 
    public String toString() { 
    return describe(); 
    } 
} 

class Frosted extends Decorator { 
    public Frosted(Cake_ cake) { 
    super(cake); 
    } 
    @Override 
    public String describe() { 
    return cake.describe() + " Frosted"; 
    } 
} 

class Decorated extends Decorator { 
    public Decorated(Cake_ cake) { 
    super(cake); 
    } 
    @Override 
    public String describe() { 
    return cake.describe() + " Decorated"; 
    } 
} 

// For the cake-building assembly line: 

class CreateCakes implements Supplier<Cake> { 
    private int id; 
    public CreateCakes(int id) { 
    this.id = id; 
    } 
    @Override 
    public Cake get() { 
    return new Cake(id); 
    } 
} 

class FrostCakes implements Function<Cake, Frosted> { 
    @Override 
    public Frosted apply(Cake cake) { 
    return new Frosted(cake); 
    } 
} 

class DecorateCakes implements Consumer<Frosted> { 
    public Decorated result; 
    @Override 
    public void accept(Frosted fc) { 
    result = new Decorated(fc); 
    } 
} 

public class Test { 
    public static int NUM_OF_CAKES = 20; 
    public static void main(String[] args) { 
    // Change from the default number of threads: 
    System.setProperty(
     "java.util.concurrent.ForkJoinPool" + 
     ".common.parallelism", "" + NUM_OF_CAKES); 

    // Test/demonstrate the decorator pattern: 
    List<Cake_> decorated = 
     IntStream.range(0, NUM_OF_CAKES) 
     .mapToObj(Cake::new) 
     .map(Frosted::new) 
     .map(Decorated::new) 
     .collect(Collectors.toList()); 
    decorated.forEach(System.out::println); 

    // Build cakes in parallel: 
    List<CompletableFuture<?>> futures = 
     IntStream.range(0, NUM_OF_CAKES) 
     .mapToObj(id -> new CreateCakes(id)) 
     .map(CompletableFuture::supplyAsync) 
     .thenApply(new FrostCakes()) 
     .thenAccept(new DecorateCakes()) 
     .collect(Collectors.toList()); 
    futures.forEach(CompletableFuture::join); 
    } 
} 

Ich weiß, ich bin einige grundlegende Verständnis in der Definition der futures Liste fehlt, aber ich habe es aufgenommen zu zeigen, was ich bin versuchen hier zu erreichen: eine Kuchenfabrik mit Teilen des Kuchen-Erstellungsprozesses läuft parallel.

+0

Wenn Sie nach einem Kompilierungsfehler fragen, geben Sie den Kompilierungsfehler ein. –

+0

Sie rufen thenApply() in einem Stream auf. Stream hat keine thenApply() -Methode. –

Antwort

3

wird eine schnelle Antwort auf Ihre Frage sein, dass die thenApply Linie nicht kompiliert, da das Ergebnis von der Linie oben (map(CompletableFuture::supplyAsync)) Stream<CompletableFuture<Cake>> und nicht CompletableFuture<Cake> zurückgibt. Sie müssen etwas wie map(cakeFuture -> cakeFuture.thenApply(new FrostCakes())) tun.

Aber ich denke, es gibt einen wichtigeren Punkt, der gemacht werden muss.

Wenn Ihre Beispiele für Bildungszwecke gedacht sind, würde ich empfehlen, noch einen oder zwei Tage in Vorbereitung zu investieren, und zwar genauer, wenn Sie über die Grundlagen der Stream-Operationen und CompletableFuture nachlesen.

Auf diese Weise werden Sie viel sicherer fühlen, wenn Sie Ihr Material präsentieren, aber noch wichtiger, Sie werden nicht weniger als perfekte Codebeispiele präsentieren, die möglicherweise Ihre Kollegen/Studenten-Vorstellung, wie Streams und CompletableFuture s (und sogar Dekoratoren) verwendet werden.

Ich werde einige der Dinge, die ich denke, müssen in Ihren Beispielen erneuert werden.

  1. Manuelle Einstellung der Parallelitätsebene der gemeinsamen ForkJoinPool ist nicht immer eine gute Idee. Standardmäßig wird die Anzahl der Prozessoren verwendet, die von Runtime.availableProcessors() zurückgegeben wird, was eine ziemlich gute Standardeinstellung ist. Sie müssen einen guten Grund haben, es zu etwas mehr als dem zu ändern, weil Sie in den meisten Fällen unnötigen Overhead aus der Planung und Wartung redundanter Threads einführen. Und es ist fast immer eine schlechte Idee, es auf die Anzahl der Aufgaben zu ändern, die Sie planen zu schießen (Erklärung weggelassen).
  2. Ihre Stream-Beispiele führen einige Stream-Operationen aus, enden dann mit einer Sammlung und anschließend wird eine Stream-Operation für die gesammelte Liste ausgeführt. Sie können ohne die zu listende Sammlung umgeschrieben werden, indem man die forEach direkt auf den von der letzten Zuordnung zurückgegebenen Datenstrom anwendet, und dies ist wohl eine bessere Demonstration des fließenden Programmiermodells, das Java 8-Datenströme verwendet.
  3. Ihre Beispiele führen ihre Operationen auch nicht parallel aus. Sie können das problemlos beheben, indem Sie .parallel() nach IntStream.range() hinzufügen. Wenn Sie jedoch die redundante Auflistung nicht entfernen, um von oben aufgelistet zu werden, können Sie nur durch Drucken des Listeninhalts forEach nicht erkennen, dass Sie etwas parallel ausgeführt haben.
  4. Ihre Klassen implementieren die java.util.function Schnittstellen sind nicht sehr idiomatisch. Ich würde argumentieren, dass sie durch die entsprechenden Lambda-Ausdrücke ersetzt werden sollten, auch wenn dies in Ihrem Fall zu einem vollen Lambda in Lambda führen könnte, wenn Ihr zweites Beispiel nicht leicht umgeschrieben wird.
  5. CompletableFuture.thenAccept gibt eine CompletableFuture<Void> zurück, so dass Sie in dieser Phase Ihrer Stream-Verarbeitung die Referenzen zu den erstellten, mattierten und dekorierten Kuchen verlieren.Dies kann in Ordnung sein, wenn Sie sich nicht darum kümmern, oder wenn Sie etwas über sie in der Dekorationslogik protokollieren, aber Ihr Beispiel kann leicht Leute irreführen, dass die endgültig gesammelte Liste von CompletableFuture s verwendet werden kann, um die Kuchen zu erreichen (nach alle, die seinen Kuchen nicht essen und auch nicht essen wollen).

So Ihr erstes Beispiel kann so etwas wie

IntStream.range(0, NUM_OF_CAKES) 
     .parallel() 
     .mapToObj(Cake::new) 
     .map(Frosted::new) 
     .map(Decorated::new) 
     .forEach(System.out::println); 

Hinweis sehen, wie der Kuchen-IDs wird nicht wegen der parallelen Ausführung bestellt.