Design Patterns Used in the CompletableFuture Examples
// UserAccountBuilder and DashboardBuilder use the Builder pattern
UserAccount.builder()
.user(user)
.account(account)
.build();
Dashboard.builder()
.user(userAccount.getUser())
.account(userAccount.getAccount())
.orders(orders)
.build();
// Static factory methods creating different types of futures
CompletableFuture.supplyAsync(() -> "Hello") // Factory method
CompletableFuture.completedFuture("Invalid") // Pre-completed factory
CompletableFuture.allOf(futures) // Composite factory
CompletableFuture.anyOf(futures) // Composite factory
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(String::toUpperCase) // Handler 1
.thenApply(s -> s + " Java") // Handler 2
.thenApply(result -> "Final: " + result) // Handler 3
.exceptionally(ex -> "Fallback"); // Error Handler
future.whenComplete((result, ex) -> {
// This is an observer/callback that gets notified
// when the future completes
if (ex != null) {
log("Error: " + ex.getMessage());
} else {
log("Success: " + result);
}
});
// The skeleton of parallel processing is defined
List<CompletableFuture<Integer>> futures = data.stream()
.map(item -> CompletableFuture.supplyAsync(() -> {
System.out.println("Processing " + item);
return processItem(item); // Subclasses/implementations provide this
}, executor))
.collect(Collectors.toList());
public final void process() {
validate();
calculateTotal();
applyDiscounts();
chargePayment();
ship();
}
interface Coffee
{ double
cost();
String desc(); }
class SimpleCoffee
implements Coffee { ... }
abstract class CoffeeDecorator implements
Coffee {
protected Coffee c;
public CoffeeDecorator(Coffee c) { this.c = c; }
public double cost() { return c.cost(); }
public String desc() { return c.desc(); }
}
class MilkDecorator
extends CoffeeDecorator {
public MilkDecorator(Coffee c) { super(c); }
public double cost() { return super.cost() + 0.5; }
public String desc() { return super.desc() + " +
Milk"; }
}
Use when:
· You need to add behavior at runtime, per object.
· Subclassing would lead to a combinatorial explosion of classes.
Builder Pattern
Intent: Separate the construction of a complex object from its representation so the same
construction process can create different representations.linkedin
Structure:
class Pizza {
private final List<String> toppings;
private final String size;
// private constructor
private Pizza(Builder b) {
this.toppings = List.copyOf(b.toppings);
this.size = b.size;
}
static class Builder {
private List<String> toppings = new ArrayList<>();
private String size = "M";
public Builder addTopping(String t) {
toppings.add(t);
return this;
}
public Builder size(String s) {
size = s;
return this;
}
public Pizza build() {
return new Pizza(this);
}
}
}
// Usage
Pizza p = new Pizza.Builder()
.size("L")
.addTopping("Cheese")
.addTopping("Pepperoni")
.build();