diff --git a/csharp.xUnit/GildedRose/AgedBrieUpdater.cs b/csharp.xUnit/GildedRose/AgedBrieUpdater.cs new file mode 100644 index 00000000..955f2d96 --- /dev/null +++ b/csharp.xUnit/GildedRose/AgedBrieUpdater.cs @@ -0,0 +1,16 @@ +namespace GildedRoseKata; + +public class AgedBrieUpdater : ItemUpdaterBase +{ + public override string Name => "Aged Brie"; + + public override void Update(Item item) + { + item.Quality = ClampQuality(item.Quality + 1); + item.SellIn -= 1; + if (item.SellIn < 0) + { + item.Quality = ClampQuality(item.Quality + 1); + } + } +} diff --git a/csharp.xUnit/GildedRose/BackstagePassUpdater.cs b/csharp.xUnit/GildedRose/BackstagePassUpdater.cs new file mode 100644 index 00000000..c1c1f4af --- /dev/null +++ b/csharp.xUnit/GildedRose/BackstagePassUpdater.cs @@ -0,0 +1,17 @@ +namespace GildedRoseKata; + +public class BackstagePassUpdater : ItemUpdaterBase +{ + public override string Name => "Backstage passes to a TAFKAL80ETC concert"; + + public override void Update(Item item) + { + var increase = item.SellIn <= 5 ? 3 : item.SellIn <= 10 ? 2 : 1; + item.Quality = ClampQuality(item.Quality + increase); + item.SellIn -= 1; + if (item.SellIn < 0) + { + item.Quality = 0; + } + } +} diff --git a/csharp.xUnit/GildedRose/IItemUpdater.cs b/csharp.xUnit/GildedRose/IItemUpdater.cs new file mode 100644 index 00000000..c7a0a823 --- /dev/null +++ b/csharp.xUnit/GildedRose/IItemUpdater.cs @@ -0,0 +1,10 @@ +namespace GildedRoseKata; + +public interface IItemUpdater +{ + /// The exact item name this strategy handles ("default" for the fallback). + string Name { get; } + + /// Applies one full day: quality change + SellIn change + clamping. + void Update(Item item); +} diff --git a/csharp.xUnit/GildedRose/IItemUpdaterFactory.cs b/csharp.xUnit/GildedRose/IItemUpdaterFactory.cs new file mode 100644 index 00000000..66855fe6 --- /dev/null +++ b/csharp.xUnit/GildedRose/IItemUpdaterFactory.cs @@ -0,0 +1,6 @@ +namespace GildedRoseKata; + +public interface IItemUpdaterFactory +{ + IItemUpdater Get(Item item); +} diff --git a/csharp.xUnit/GildedRose/ItemUpdaterBase.cs b/csharp.xUnit/GildedRose/ItemUpdaterBase.cs new file mode 100644 index 00000000..a973b8d4 --- /dev/null +++ b/csharp.xUnit/GildedRose/ItemUpdaterBase.cs @@ -0,0 +1,15 @@ +using System; + +namespace GildedRoseKata; + +public abstract class ItemUpdaterBase : IItemUpdater +{ + public const int MinQuality = 0; + public const int MaxQuality = 50; + + public abstract string Name { get; } + + public abstract void Update(Item item); + + protected static int ClampQuality(int quality) => Math.Clamp(quality, MinQuality, MaxQuality); +} diff --git a/csharp.xUnit/GildedRose/ItemUpdaterFactory.cs b/csharp.xUnit/GildedRose/ItemUpdaterFactory.cs new file mode 100644 index 00000000..a6bc365d --- /dev/null +++ b/csharp.xUnit/GildedRose/ItemUpdaterFactory.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; + +namespace GildedRoseKata; + +public class ItemUpdaterFactory : IItemUpdaterFactory +{ + public const string ConjuredPrefix = "Conjured"; + private const string DefaultName = "default"; + + private readonly Dictionary _updatersByName; + private readonly IItemUpdater _fallback; + + public ItemUpdaterFactory(IEnumerable updaters) + { + _updatersByName = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var updater in updaters) + { + _updatersByName[updater.Name] = updater; + } + + if (!_updatersByName.TryGetValue(DefaultName, out _fallback)) + { + throw new ArgumentException($"A \"{DefaultName}\" fallback updater is required.", nameof(updaters)); + } + } + + public static ItemUpdaterFactory Default() => new(new IItemUpdater[] + { + new NormalItemUpdater(), + new AgedBrieUpdater(), + new SulfurasUpdater(), + new BackstagePassUpdater(), + }); + + public IItemUpdater Get(Item item) + { + if (_updatersByName.TryGetValue(item.Name, out var updater)) + { + return updater; + } + + if (item.Name.StartsWith(ConjuredPrefix, StringComparison.OrdinalIgnoreCase) && + _updatersByName.TryGetValue(ConjuredPrefix, out var conjured)) + { + return conjured; + } + + return _fallback; + } +} diff --git a/csharp.xUnit/GildedRose/NormalItemUpdater.cs b/csharp.xUnit/GildedRose/NormalItemUpdater.cs new file mode 100644 index 00000000..5f35bf3a --- /dev/null +++ b/csharp.xUnit/GildedRose/NormalItemUpdater.cs @@ -0,0 +1,16 @@ +namespace GildedRoseKata; + +public class NormalItemUpdater : ItemUpdaterBase +{ + public override string Name => "default"; + + public override void Update(Item item) + { + item.Quality = ClampQuality(item.Quality - 1); + item.SellIn -= 1; + if (item.SellIn < 0) + { + item.Quality = ClampQuality(item.Quality - 1); + } + } +} diff --git a/csharp.xUnit/GildedRose/SulfurasUpdater.cs b/csharp.xUnit/GildedRose/SulfurasUpdater.cs new file mode 100644 index 00000000..66ee371e --- /dev/null +++ b/csharp.xUnit/GildedRose/SulfurasUpdater.cs @@ -0,0 +1,11 @@ +namespace GildedRoseKata; + +public class SulfurasUpdater : ItemUpdaterBase +{ + public override string Name => "Sulfuras, Hand of Ragnaros"; + + public override void Update(Item item) + { + // Legendary item: Quality stays at 80 and SellIn never changes. + } +}