Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
125bb7ad01 | ||
|
|
d14614f78a | ||
|
|
578162851b | ||
|
|
f9c3c68e79 | ||
|
|
f9bb7b982a | ||
|
|
c153aba61e |
@@ -0,0 +1,79 @@
|
||||
// Example 3 — Ice cream shop demo of Slice with Lambda (C#)
|
||||
//
|
||||
// Condensed mechanics: https://sammancoaching.org/refactorings/slice.html
|
||||
// (where that page says mock, we use a lambda instead).
|
||||
//
|
||||
// Third repetition, same complexity as ScheduleWatering in WeatherStation.cs
|
||||
// (guard -> one dependency call -> map to enum -> constant tail), so
|
||||
// participants can practice extracting a lambda a second time. The C#
|
||||
// delegate syntax is worth the repetition:
|
||||
//
|
||||
// Func<byte> readTemperature = () => _thermometer.Read();
|
||||
//
|
||||
// Cast:
|
||||
// Thermometer — hardware-ish dependency. Read() returns a raw
|
||||
// byte value (not calibrated, the client decides
|
||||
// what it means).
|
||||
// Warmth — enum. Reading > 25 => Warm, otherwise Cold.
|
||||
// (Sunny/Shady's cousin from WeatherStation.cs.)
|
||||
// ThawAmount — enum. None / Medium / Low.
|
||||
// IceCreamShop — the client. Owns the _thawedToday flag, so the
|
||||
// guard is about client state, not sensor state.
|
||||
|
||||
using System;
|
||||
|
||||
public enum Warmth
|
||||
{
|
||||
Cold,
|
||||
Warm
|
||||
}
|
||||
|
||||
public enum ThawAmount
|
||||
{
|
||||
None,
|
||||
Medium,
|
||||
Low
|
||||
}
|
||||
|
||||
public class Thermometer
|
||||
{
|
||||
private readonly Random _rng = new Random(); // stand-in for the hardware
|
||||
|
||||
public byte Read()
|
||||
{
|
||||
return (byte)_rng.Next(50); // raw sensor value
|
||||
}
|
||||
}
|
||||
|
||||
public class IceCreamShop
|
||||
{
|
||||
private readonly Thermometer _thermometer;
|
||||
private bool _thawedToday; // event-style client state
|
||||
|
||||
public IceCreamShop(Thermometer thermometer)
|
||||
{
|
||||
_thermometer = thermometer;
|
||||
}
|
||||
|
||||
public ThawAmount ThawStock()
|
||||
{
|
||||
if (_thawedToday) // guard: client state, stays
|
||||
{
|
||||
return ThawAmount.None;
|
||||
}
|
||||
|
||||
_thawedToday = true; // set only after thawing
|
||||
byte temperature = _thermometer.Read(); // middle: unwanted dependency
|
||||
Warmth warmth = ToWarmth(temperature);
|
||||
if (warmth == Warmth.Warm) // tail: complex behaviour below
|
||||
{ // the extraction point
|
||||
return ThawAmount.Medium;
|
||||
}
|
||||
return ThawAmount.Low;
|
||||
}
|
||||
|
||||
private static Warmth ToWarmth(byte temperature)
|
||||
{
|
||||
return temperature > 25 ? Warmth.Warm : Warmth.Cold;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// Example 1 — Metaphor-free demo of Slice with Lambda (C#)
|
||||
//
|
||||
// Condensed mechanics: https://sammancoaching.org/refactorings/slice.html
|
||||
// (where that page says mock, we use a lambda instead).
|
||||
//
|
||||
// Shape of the method (per zeroth-draft-of-refactoring-pattern.md):
|
||||
//
|
||||
// guard clauses — must run first
|
||||
// middle: dependency — one call on an object we don't want in the
|
||||
// extracted method
|
||||
// tail: depends on b — uses the value the dependency produced
|
||||
//
|
||||
// Deliberately NOT a loop: this is the smallest method that shows the
|
||||
// mechanics. The dependency both validates its precondition (using the
|
||||
// parameter a) and produces b — one expression, one thing to move into
|
||||
// the lambda.
|
||||
//
|
||||
// Unwanted dependency: SomeDependency — might throw when its precondition
|
||||
// on a is not satisfied, and returns an unpredictable value. An approval
|
||||
// test on Calculate is flaky until the lambda is stubbed.
|
||||
|
||||
using System;
|
||||
|
||||
public class Calculator
|
||||
{
|
||||
private readonly SomeDependency _someDependency;
|
||||
|
||||
public Calculator(SomeDependency someDependency)
|
||||
{
|
||||
_someDependency = someDependency;
|
||||
}
|
||||
|
||||
public int Calculate(int a)
|
||||
{
|
||||
if (a < 0) return 0; // guard clause: stays
|
||||
int b = _someDependency.mightThrowWhenPreconditionNotSatisfied(a); // middle: unwanted dependency
|
||||
int c = a + b; // tail: depends on b
|
||||
return c * 2;
|
||||
}
|
||||
}
|
||||
|
||||
public class SomeDependency
|
||||
{
|
||||
private readonly Random _rng = new Random(); // stand-in for unpredictable computation
|
||||
|
||||
public int mightThrowWhenPreconditionNotSatisfied(int a)
|
||||
{
|
||||
if (a > 99)
|
||||
{
|
||||
throw new InvalidOperationException("precondition not satisfied");
|
||||
}
|
||||
return _rng.Next(100);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
# Slice with Lambda — C# demo examples
|
||||
|
||||
Three C# demo examples for the **Slice with Lambda** refactoring.
|
||||
Condensed mechanics: https://www.sammancoaching.org/refactorings/slice.html
|
||||
(where that page says mock, we use a lambda instead).
|
||||
Full Java walkthrough: `../../storyboard/storyboard.md`.
|
||||
Slides: `~/qwan/slides-rjs/decks/refactoring/slice-with-lambda/slides.md`, served at http://localhost:9001/#/refactoring/slice-with-lambda.
|
||||
|
||||
## The three examples
|
||||
|
||||
| file | guard (client state) | unwanted dependency | lambda | tail |
|
||||
|---|---|---|---|---|
|
||||
| `MetaphorFree.cs` | `if (a < 0) return 0;` | `_someDependency.mightThrowWhenPreconditionNotSatisfied(a)` | `Func<int, int> readB = a => ...` | `int c = a + b; return c * 2;` |
|
||||
| `WeatherStation.cs` | `if (_wateredToday)` | `_lightSensor.Read()` (byte) | `Func<byte> readLight = () => ...` | sunny -> `WateringDuration.Medium`, shady -> `Short` (reading > 128 => `SunExposure.Sunny`) |
|
||||
| `IceCreamShop.cs` | `if (_thawedToday)` | `_thermometer.Read()` (byte) | `Func<byte> readTemperature = () => ...` | warm -> `ThawAmount.Medium`, cold -> `Low` (temperature > 25 => `Warmth.Warm`) |
|
||||
|
||||
In all three, the guard reads client state (a parameter or a flag the client
|
||||
owns), so it stays in the extracted method. Only the dependency call moves
|
||||
into the lambda.
|
||||
|
||||
## The ramp
|
||||
|
||||
`MetaphorFree.cs` is the smallest possible shape: no loop, one dependency
|
||||
call, a tail of one arithmetic step. It carries the capture-to-parameter
|
||||
lesson: the lambda takes `a` as its parameter (`readB(a)`) because a lambda
|
||||
moved up into the function head cannot capture method parameters. Its
|
||||
step-4 stub tests include a throwing stub.
|
||||
|
||||
`WeatherStation.cs` and `IceCreamShop.cs` are same-shape domain
|
||||
repetitions — guard, one dependency call, map to enum, constant tail — so
|
||||
participants can practice extracting the lambda twice. The C# delegate
|
||||
syntax (`Func<byte>`) is worth the repetition.
|
||||
|
||||
## Same three regions every time
|
||||
|
||||
Each example deliberately has the same three regions:
|
||||
|
||||
- **before**: guard clauses
|
||||
- **middle**: one dependency call
|
||||
- **after**: tail that depends on the value the dependency produced
|
||||
|
||||
Matching the pattern: extract the dependency-using code into a lambda,
|
||||
slide the definition to the top, extract the method below it, pass the
|
||||
lambda as parameter, test with pure lambda stubs.
|
||||
@@ -0,0 +1,71 @@
|
||||
// Example 2 — Weather Station domain demo of Slice with Lambda (C#)
|
||||
//
|
||||
// Condensed mechanics: https://sammancoaching.org/refactorings/slice.html
|
||||
// (where that page says mock, we use a lambda instead).
|
||||
//
|
||||
// Cast (Head First Design Patterns Weather Station, reduced):
|
||||
// LightSensor — hardware-ish dependency. Read() returns a byte.
|
||||
// SunExposure — enum. Reading > 128 => Sunny, otherwise Shady.
|
||||
// WateringDuration — enum. None / Medium / Short.
|
||||
// (Long is YAGNI until the for-loop version of this
|
||||
// exercise exists.)
|
||||
// SprinklerController — the client. Owns the _wateredToday flag, so the
|
||||
// guard is about client state, not sensor state.
|
||||
|
||||
using System;
|
||||
|
||||
public enum SunExposure
|
||||
{
|
||||
Shady,
|
||||
Sunny
|
||||
}
|
||||
|
||||
public enum WateringDuration
|
||||
{
|
||||
None,
|
||||
Medium,
|
||||
Short
|
||||
}
|
||||
|
||||
public class LightSensor
|
||||
{
|
||||
private readonly Random _rng = new Random(); // stand-in for hardware noise
|
||||
|
||||
public byte Read()
|
||||
{
|
||||
return (byte)_rng.Next(256);
|
||||
}
|
||||
}
|
||||
|
||||
public class SprinklerController
|
||||
{
|
||||
private readonly LightSensor _lightSensor;
|
||||
private bool _wateredToday; // event-style client state
|
||||
|
||||
public SprinklerController(LightSensor lightSensor)
|
||||
{
|
||||
_lightSensor = lightSensor;
|
||||
}
|
||||
|
||||
public WateringDuration ScheduleWatering()
|
||||
{
|
||||
if (_wateredToday) // guard: client state, stays
|
||||
{
|
||||
return WateringDuration.None;
|
||||
}
|
||||
|
||||
_wateredToday = true; // set only after watering
|
||||
byte reading = _lightSensor.Read(); // middle: unwanted dependency
|
||||
SunExposure exposure = ToSunExposure(reading);
|
||||
if (exposure == SunExposure.Sunny) // tail: complex behaviour below
|
||||
{ // the extraction point
|
||||
return WateringDuration.Medium;
|
||||
}
|
||||
return WateringDuration.Short;
|
||||
}
|
||||
|
||||
private static SunExposure ToSunExposure(byte reading)
|
||||
{
|
||||
return reading > 128 ? SunExposure.Sunny : SunExposure.Shady;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Example 3 solution — Ice cream shop demo of Slice with Lambda (C#), after
|
||||
//
|
||||
// Lambda extracted (step 1), slid to the top (step 2), method extracted
|
||||
// below the guard (step 3). The constructor now takes the lambda directly,
|
||||
// so tests construct with new IceCreamShop(() => 30).
|
||||
//
|
||||
// Condensed mechanics: https://sammancoaching.org/refactorings/slice.html
|
||||
// (where that page says mock, we use a lambda instead).
|
||||
|
||||
using System;
|
||||
|
||||
public enum Warmth
|
||||
{
|
||||
Cold,
|
||||
Warm
|
||||
}
|
||||
|
||||
public enum ThawAmount
|
||||
{
|
||||
None,
|
||||
Medium,
|
||||
Low
|
||||
}
|
||||
|
||||
public class Thermometer
|
||||
{
|
||||
private readonly Random _rng = new Random(); // stand-in for the hardware
|
||||
|
||||
public byte Read()
|
||||
{
|
||||
return (byte)_rng.Next(50); // raw sensor value
|
||||
}
|
||||
}
|
||||
|
||||
public class IceCreamShop
|
||||
{
|
||||
private readonly Func<byte> _readTemperature; // the thermometer, as a lambda
|
||||
private bool _thawedToday; // event-style client state
|
||||
|
||||
public IceCreamShop(Func<byte> readTemperature)
|
||||
{
|
||||
_readTemperature = readTemperature;
|
||||
}
|
||||
|
||||
public ThawAmount ThawStock()
|
||||
{
|
||||
Func<byte> readTemperature = () => _readTemperature(); // lambda at the top
|
||||
return ThawStockTail(readTemperature); // pass the lambda on
|
||||
}
|
||||
|
||||
private ThawAmount ThawStockTail(Func<byte> readTemperature)
|
||||
{
|
||||
if (_thawedToday) // guard: client state, stays
|
||||
{
|
||||
return ThawAmount.None;
|
||||
}
|
||||
|
||||
_thawedToday = true; // set only after thawing
|
||||
byte temperature = readTemperature(); // middle: the stubbed lambda
|
||||
Warmth warmth = ToWarmth(temperature);
|
||||
if (warmth == Warmth.Warm) // tail: complex behaviour below
|
||||
{ // the extraction point
|
||||
return ThawAmount.Medium;
|
||||
}
|
||||
return ThawAmount.Low;
|
||||
}
|
||||
|
||||
private static Warmth ToWarmth(byte temperature)
|
||||
{
|
||||
return temperature > 25 ? Warmth.Warm : Warmth.Cold;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Example 1 solution — Metaphor-free demo of Slice with Lambda (C#), after
|
||||
//
|
||||
// Lambda extracted (step 1), slid to the top (step 2), method extracted
|
||||
// below the guard (step 3), overload seam added (step 3b), b inlined
|
||||
// (final cleanup).
|
||||
//
|
||||
// Condensed mechanics: https://sammancoaching.org/refactorings/slice.html
|
||||
// (where that page says mock, we use a lambda instead).
|
||||
|
||||
using System;
|
||||
|
||||
public class Calculator
|
||||
{
|
||||
private readonly SomeDependency _someDependency;
|
||||
|
||||
public Calculator(SomeDependency someDependency)
|
||||
{
|
||||
_someDependency = someDependency;
|
||||
}
|
||||
|
||||
public int Calculate(int a) // overload: callers unchanged
|
||||
{
|
||||
return Calculate(a, a =>
|
||||
_someDependency.mightThrowWhenPreconditionNotSatisfied(a));
|
||||
} // the real λ, baked into the head
|
||||
|
||||
public int Calculate(int a, Func<int, int> readB) // the seam: λ is a parameter
|
||||
{
|
||||
if (a < 0) return 0; // guard clause: stays
|
||||
return CalculateTail(a, readB);
|
||||
}
|
||||
|
||||
private int CalculateTail(int a, Func<int, int> readB)
|
||||
{
|
||||
int c = a + readB(a); // b inlined
|
||||
return c * 2;
|
||||
}
|
||||
}
|
||||
|
||||
public class SomeDependency
|
||||
{
|
||||
private readonly Random _rng = new Random(); // stand-in for unpredictable computation
|
||||
|
||||
public int mightThrowWhenPreconditionNotSatisfied(int a)
|
||||
{
|
||||
if (a > 99)
|
||||
{
|
||||
throw new InvalidOperationException("precondition not satisfied");
|
||||
}
|
||||
return _rng.Next(100);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Tests for the three Slice-with-Lambda solutions — step 4: the lambda as a stub.
|
||||
//
|
||||
// Condensed mechanics: https://sammancoaching.org/refactorings/slice.html
|
||||
// (where that page says mock, we use a lambda instead).
|
||||
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
public class SliceWithLambdaTests
|
||||
{
|
||||
// --- Example 1: metaphor-free (Calculator) ---
|
||||
|
||||
[Fact]
|
||||
public void Guards_against_negative_input()
|
||||
{
|
||||
var calc = new Calculator(new SomeDependency()); // the real, flaky one...
|
||||
Assert.Equal(0, calc.Calculate(-1, a => 42)); // ...never asked: stub not invoked
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Throws_when_precondition_not_satisfied()
|
||||
{
|
||||
var calc = new Calculator(new SomeDependency());
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => calc.Calculate(7, a => throw new InvalidOperationException()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Combines_input_and_the_stubbed_value()
|
||||
{
|
||||
var calc = new Calculator(new SomeDependency());
|
||||
Assert.Equal(84, calc.Calculate(0, a => 42)); // stub through the seam
|
||||
}
|
||||
|
||||
// --- Example 2: Weather Station (SprinklerController) ---
|
||||
|
||||
[Fact]
|
||||
public void Skips_watering_when_already_watered()
|
||||
{
|
||||
var controller = new SprinklerController(() => 200); // pure lambda: no sensor
|
||||
controller.ScheduleWatering(); // water once...
|
||||
Assert.Equal(WateringDuration.None, controller.ScheduleWatering());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Waters_longer_in_full_sun()
|
||||
{
|
||||
var controller = new SprinklerController(() => 200); // sunny: > 128
|
||||
Assert.Equal(WateringDuration.Medium, controller.ScheduleWatering());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Waters_less_in_the_shade()
|
||||
{
|
||||
var controller = new SprinklerController(() => 10); // shady: ≤ 128
|
||||
Assert.Equal(WateringDuration.Short, controller.ScheduleWatering());
|
||||
}
|
||||
|
||||
// --- Example 3: Ice Cream Shop (IceCreamShop) ---
|
||||
|
||||
[Fact]
|
||||
public void Skips_thawing_when_already_thawed()
|
||||
{
|
||||
var shop = new IceCreamShop(() => 30); // pure lambda: no freezer
|
||||
shop.ThawStock(); // thaw once...
|
||||
Assert.Equal(ThawAmount.None, shop.ThawStock());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Thaws_more_when_warm()
|
||||
{
|
||||
var shop = new IceCreamShop(() => 30); // warm: > 25
|
||||
Assert.Equal(ThawAmount.Medium, shop.ThawStock());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Thaws_less_when_cold()
|
||||
{
|
||||
var shop = new IceCreamShop(() => 5); // cold: ≤ 25
|
||||
Assert.Equal(ThawAmount.Low, shop.ThawStock());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Example 2 solution — Weather Station domain demo of Slice with Lambda (C#), after
|
||||
//
|
||||
// Lambda extracted (step 1), slid to the top (step 2), method extracted
|
||||
// below the guard (step 3). The constructor now takes the lambda directly,
|
||||
// so tests construct with new SprinklerController(() => 200).
|
||||
//
|
||||
// Condensed mechanics: https://sammancoaching.org/refactorings/slice.html
|
||||
// (where that page says mock, we use a lambda instead).
|
||||
|
||||
using System;
|
||||
|
||||
public enum SunExposure
|
||||
{
|
||||
Shady,
|
||||
Sunny
|
||||
}
|
||||
|
||||
public enum WateringDuration
|
||||
{
|
||||
None,
|
||||
Medium,
|
||||
Short
|
||||
}
|
||||
|
||||
public class LightSensor
|
||||
{
|
||||
private readonly Random _rng = new Random(); // stand-in for hardware noise
|
||||
|
||||
public byte Read()
|
||||
{
|
||||
return (byte)_rng.Next(256);
|
||||
}
|
||||
}
|
||||
|
||||
public class SprinklerController
|
||||
{
|
||||
private readonly Func<byte> _readLight; // the sensor, as a lambda
|
||||
private bool _wateredToday; // event-style client state
|
||||
|
||||
public SprinklerController(Func<byte> readLight)
|
||||
{
|
||||
_readLight = readLight;
|
||||
}
|
||||
|
||||
public WateringDuration ScheduleWatering()
|
||||
{
|
||||
Func<byte> readLight = () => _readLight(); // lambda definition at the top
|
||||
return ScheduleWateringTail(readLight); // pass the lambda on
|
||||
}
|
||||
|
||||
private WateringDuration ScheduleWateringTail(Func<byte> readLight)
|
||||
{
|
||||
if (_wateredToday) // guard: client state, stays
|
||||
{
|
||||
return WateringDuration.None;
|
||||
}
|
||||
|
||||
_wateredToday = true; // set only after watering
|
||||
byte reading = readLight(); // middle: the stubbed lambda
|
||||
SunExposure exposure = ToSunExposure(reading);
|
||||
if (exposure == SunExposure.Sunny) // tail: complex behaviour below
|
||||
{ // the extraction point
|
||||
return WateringDuration.Medium;
|
||||
}
|
||||
return WateringDuration.Short;
|
||||
}
|
||||
|
||||
private static SunExposure ToSunExposure(byte reading)
|
||||
{
|
||||
return reading > 128 ? SunExposure.Sunny : SunExposure.Shady;
|
||||
}
|
||||
}
|
||||
@@ -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); the slides are at `~/qwan/slides-rjs/decks/refactoring/slice-with-lambda/slides.md`, served at http://localhost:9001/#/refactoring/slice-with-lambda.
|
||||
|
||||
## How to read the diagrams
|
||||
|
||||
|
||||
@@ -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<int, int> 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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user