Add three C# demo examples for Slice with Lambda
- MetaphorFree.cs: guard -> SomeDependency.mightThrowWhenPreconditionNotSatisfied(a) -> tail, capture-to-parameter story for the deck - WeatherStation.cs: SprinklerController, LightSensor.Read() byte -> SunExposure, tail branches - IceCreamShop.cs: third repetition, Thermometer byte -> Warmth, tail branches
This commit is contained in:
@@ -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,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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user