diff --git a/ideas/split-loop-with-lambda/storyboard/storyboard.md b/ideas/split-loop-with-lambda/storyboard/storyboard.md index 688da93..9d4f1b9 100644 --- a/ideas/split-loop-with-lambda/storyboard/storyboard.md +++ b/ideas/split-loop-with-lambda/storyboard/storyboard.md @@ -1,6 +1,6 @@ -# Split Loop with Lambda — storyboard / handout +# Slice with Lambda — storyboard / handout -Step-by-step mechanics of the **Split Loop with Lambda** refactoring, one diagram per +Step-by-step mechanics of the **Slice with Lambda** refactoring (formerly called "Split Loop with Lambda"), one diagram per step, each step followed by the exact code (full before/after, highlights as `←` comments). Written to be sliced into slides later; until then it works as a handout. @@ -8,6 +8,7 @@ Sources: `../zeroth-draft-of-refactoring-pattern.md` (the mechanics), `../split-loop-with-lambda-learning-hour.md` (where we stop: after step 3 + the test), `../exercise/rehearsal-findings.md` (the drift anti-pattern and the "both calls!" caution). Java only. Pristine start: `../exercise/java`, end state: `../exercise/solution`. +C# demo examples live at `../exercise/csharp/` (MetaphorFree.cs, WeatherStation.cs, IceCreamShop.cs), and there is a reveal.js deck at `/home/cardfanatic/qwan/slides-rjs/decks/slice-with-lambda/slides.md`. ## How to read the diagrams diff --git a/ideas/split-loop-with-lambda/zeroth-draft-of-refactoring-pattern.md b/ideas/split-loop-with-lambda/zeroth-draft-of-refactoring-pattern.md index a8dba2e..1c98e64 100644 --- a/ideas/split-loop-with-lambda/zeroth-draft-of-refactoring-pattern.md +++ b/ideas/split-loop-with-lambda/zeroth-draft-of-refactoring-pattern.md @@ -4,7 +4,7 @@ # Introduction -This is a zeroth draft. Our meta-goals are being more explicit in naming the refactorings and patterns we used to ourselves, and enable others to name the patterns, so they can reproduce the mechanics reliably on their own. +This is a zeroth draft of the **Slice with Lambda** refactoring pattern (formerly called "Split Loop with Lambda"). Our meta-goals are being more explicit in naming the refactorings and patterns we used to ourselves, and enable others to name the patterns, so they can reproduce the mechanics reliably on their own. Initial literature survey (see related patterns below) has not yet revealed an existing Refactoring Pattern description. In order to fit some or all of this refactoring in a learning hour, it is worth writing down what we mean, with examples (so, in case the resulting exercise does not fit in a learning hour, we have a blog post to refer to for more details and/or recapturing the mechanics). @@ -24,7 +24,39 @@ Split loop suggests “‘introduce variable’ refactorings and ‘slide statem 3. Extract the method below the slides statement. You now have a new method, that takes the lambda as a parameter 4. Move the method to another class where the unwanted dependency is not present 5. Move and/or create tests, using the lambda as a stub instead of a concrete invocation of the unwanted dependency. -6. Check that “everything that could possibly break” is tested \- the extracted lambda allows more combinations to be easily tested. +6. Check that "everything that could possibly break" is tested \- the extracted lambda allows more combinations to be easily tested. + +## The no-loop minimal shape + +The mechanics above are usually applied inside a loop, but the pattern does not require one. The minimal shape is a method consisting of guard clauses, then ONE call on an unwanted dependency in the middle, then a tail that depends on the value the call produced: + +```csharp +public int Calculate(int a) +{ + if (a < 0) return 0; + int b = _someDependency.mightThrowWhenPreconditionNotSatisfied(a); + int c = a + b; + return c * 2; +} +``` + +The same mechanics apply: extract the call into a lambda, slide the lambda definition to the top, extract the method below the slide statement with the lambda as a parameter. There is simply no loop to split — hence "Slice". + +### Capture-to-parameter: the lambda cannot capture the method's parameters + +When the lambda is moved up into the function head (as a parameter via the overload seam), it cannot capture the method's parameters — they are not in scope there — so the lambda must take them explicitly: + +```csharp +Func readB = a => _someDependency.mightThrowWhenPreconditionNotSatisfied(a); +``` + +and every invocation becomes + +```csharp +int b = readB(a); +``` + +Note that the Java storyboard lambda already took `daysForward` as its parameter, so the Java walkthrough does not hit this; the C# metaphor-free demo (`exercise/csharp/MetaphorFree.cs`) does. # Consequences / resulting context.