Good Code
The good version uses a loop because the operation has branching and a named accumulation step. Each review question has a place to attach.
Lesson 07
Use streams for clear transformations and loops when branching, validation, or naming intermediate steps makes the review easier.
public Money discountFor(List<OrderLine> lines) {
Money discount = Money.zero();
for (OrderLine line : lines) {
if (!line.isDiscountable()) {
continue;
}
Money lineDiscount = policy.discountFor(line);
discount = discount.plus(lineDiscount);
}
return discount;
}public Money discountFor(List<OrderLine> lines) {
AtomicReference<Money> discount = new AtomicReference<>(Money.zero());
lines.stream()
.filter(line -> line.isDiscountable())
.peek(line -> audit.log(line.id()))
.map(line -> policy.discountFor(line))
.forEach(value -> discount.set(discount.get().plus(value)));
return discount.get();
}The good version uses a loop because the operation has branching and a named accumulation step. Each review question has a place to attach.
The bad version hides mutation inside a stream pipeline and uses peek for side effects. The stream looks functional, but it is harder to reason about.