Initial commit

This commit is contained in:
Your Name
2026-09-29 16:28:20 +00:00
commit 17e5d25b39
28 changed files with 961 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
namespace IceCreamBuyers
{
public class Buyer
{
public Buyer(string name, IReadOnlyList<IceCream> favourites)
{
Name = name;
Favourites = favourites;
}
public string Name { get; }
public IReadOnlyList<IceCream> Favourites { get; }
public int ScoopsOf(IceCream flavour)
{
return 2;
}
}
}
+33
View File
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
namespace IceCreamBuyers
{
public class Buyers
{
private readonly PriceList priceList = new();
private readonly List<Buyer> buyers = new()
{
new("Anke", new List<IceCream> { IceCream.Strawberry, IceCream.Vanilla }),
new("Bram", new List<IceCream> { IceCream.Chocolate }),
new("Celine", new List<IceCream> { IceCream.Vanilla, IceCream.Chocolate }),
};
public int ShoppingListTotal(string buyerName)
{
var buyer = ByName(buyerName);
var total = 0;
foreach (var flavour in buyer.Favourites)
{
total += buyer.ScoopsOf(flavour) * priceList.PriceFor(flavour);
}
return total;
}
private Buyer ByName(string name)
{
return buyers.Find(b => b.Name == name)
?? throw new InvalidOperationException($"Unknown buyer {name}.");
}
}
}
+9
View File
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
+9
View File
@@ -0,0 +1,9 @@
namespace IceCreamBuyers
{
public enum IceCream
{
Strawberry,
Chocolate,
Vanilla
}
}
+27
View File
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
namespace IceCreamBuyers
{
public class PriceList
{
// placeholder implementation - real version would call the pricing service
private static readonly Dictionary<IceCream, int> BasePrices =
new()
{
{ IceCream.Strawberry, 3 },
{ IceCream.Vanilla, 2 },
{ IceCream.Chocolate, 4 },
};
public int PriceFor(IceCream flavour)
{
return BasePrices[flavour] + SurgePricing();
}
private int SurgePricing()
{
return new Random().Next(3);
}
}
}
+79
View File
@@ -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;
}
}
+54
View File
@@ -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,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
+71
View File
@@ -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;
}
}