Add split-loop-with-lambda learning hour: draft, pre-factored exercise, solution

- Samman learning hour: 5/5/10/25/5 outline; pairs stop at extracted
  method taking the lambda as a parameter
- exercise/java: pre-factored variant of the kata (Q1/Q4 dead branch
  stripped, dates as constant), approval test red/flaky by design
- exercise/solution: collectExpectedWeather(LongFunction<Boolean>) +
  no-arg overload; deterministic approval test (all-sunny lambda)
- rehearse-with-agent.md: rehearsal setup with qwen + java-refactoring-mcp
This commit is contained in:
Your Name
2026-09-27 13:30:00 +00:00
parent 1a063860c9
commit a27e32688a
18 changed files with 737 additions and 0 deletions
@@ -0,0 +1,50 @@
# Exercise folder — Split Loop with Lambda (learning hour)
Contents:
- `java/` — pre-factored exercise repo (prefactored-longrange; runs with `mvn test`)
- `solution/` — reference end state after step 3 (extracted method taking the lambda)
- `rehearse-with-agent.md` — how Willem rehearses this with qwen-code + java-refactoring-mcp
## Reference solution
See `solution/`. End state of the production code (simplified):
```java
public Map<IceCream, Integer> longRangeForecast() {
return longRangeForecast(daysForward -> {
scorer.updateSelection();
return scorer.lookupWeather(daysForward);
});
}
public Map<IceCream, Integer> longRangeForecast(
LongFunction<Boolean> dayOffsetToWeather) {
var expectedWeather = collectExpectedWeather(dayOffsetToWeather);
// ... reset loop + scoring loop, unchanged ...
}
private static List<Boolean> collectExpectedWeather(
LongFunction<Boolean> dayOffsetToWeather) {
var expectedWeather = new ArrayList<Boolean>();
for (Instant interestingDate : DATES) {
long daysForward = ChronoUnit.DAYS.between(ANCHOR_DATE, interestingDate);
expectedWeather.add(dayOffsetToWeather.apply(daysForward));
}
return expectedWeather;
}
```
Notes on the end state:
- Key move: `scorer` is used **only inside the lambda**; the extracted method
`collectExpectedWeather` has no dependency on `Scorer` at all — all its
inputs are parameters.
- The public no-arg overload keeps callers unchanged; it injects the
"real" weather lambda. The test calls the overload with a pure
`daysForward -> true` instead.
- The test is pure and glanceable: `alwaysSunny` weather gives
`{Strawberry:12, Chocolate:12, Vanilla:17, }` (approved file included).
- `scorer.updateSelection()` turned out to be deletable (it only mutates
static `Scorer.flavour` state that this method never reads). Leaving it
in the lambda is fine for step 3; deleting it is a debrief aha.