Files
learning-hour-maker/ideas/split-loop-with-lambda/exercise3/solution/README.md
T
Your Name 1189041d8e Add exercise 3 (Buyers, C#): the pure extract-lambda rep
New in-theme classes: Buyers computes a shopping-list total, PriceList
stands for the pricing service (random surge, flaky test). One loop, one
lambda-able expression — the third example teaches the move with nothing
else in the way. Pristine repo + reference solution (extract lambda,
slide, extract method with Func<IceCream,int> parameter, pure-lambda
test) + storyboard in the established style. Mirrors the kata's C#
setup (net7.0, MSTest); needs a build check on a machine with the SDK.
2026-09-28 21:48:30 +00:00

51 lines
2.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Reference solution — end state of exercise 3
Derived from [IceCreamForecasting-Refactoring-Kata](https://github.com/emilybache/IceCreamForecasting-Refactoring-Kata)
(same license as the kata applies to the copied `IceCream.cs`).
Run with `dotnet test test/BuyersTests.csproj` — the test passes deterministically
without touching the pricing service.
## What changed vs the exercise start
1. The pricing call extracted into a lambda and slid to the top:
`Func<IceCream, int> priceFor = flavour => priceList.PriceFor(flavour);`
— the invocation in the loop stays put (`priceFor(flavour)`).
2. The loop extracted to
`ShoppingListTotal(Buyer buyer, Func<IceCream, int> priceFor)` — static-free of
`PriceList`; all inputs are parameters. (C# note: this one is an instance method
because `ByName` uses the `buyers` field; the *extracted loop method itself*
touches no fields. If you want it `static`, extract the loop only and keep the
lookup in the composed method.)
3. No-arg `ShoppingListTotal(string)` overload keeps callers working; it passes the
method group `priceList.PriceFor` as the "real" pricing function.
4. The test passes a pure lambda (`_ => 2`) through the overload — deterministic,
glanceable: 2 flavours × 2 scoops × 2 = 8.
## Intermediate step (between pristine and this solution)
```csharp
public int ShoppingListTotal(string buyerName)
{
Func<IceCream, int> priceFor = flavour => priceList.PriceFor(flavour); // ← extracted & slid
var buyer = ByName(buyerName);
var total = 0;
foreach (var flavour in buyer.Favourites)
{
total += buyer.ScoopsOf(flavour) * priceFor(flavour); // ← invocation stays
}
return total;
}
```
## Variations (debrief / second round)
- `_ => 5` — everything costs 5: Anke 20, Bram 10, Celine 20.
- `flavour => flavour == IceCream.Chocolate ? 10 : 1` — chocolate is expensive.
- Delete `PriceList` entirely: the extracted method no longer knows it exists —
a step-4 move is now unblocked (move `ShoppingListTotal(Buyer, Func)` to a
class without `PriceList`).
- Role-interface next step: add `DiscountFor(IceCream)` to `PriceList` (a second
side-effectful method) and feel why one lambda is no longer enough — extract
`IPricing` instead.