This commit is contained in:
Artur Holoiad 2026-07-26 00:02:55 +01:00 committed by GitHub
commit f282495b59
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 371 additions and 88 deletions

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,17 @@
namespace GildedRoseKata;
public class ConjuredItemUpdater : ItemUpdaterBase
{
// Matched by prefix in ItemUpdaterFactory: handles every "Conjured..." item.
public override string Name => ItemUpdaterFactory.ConjuredPrefix;
public override void Update(Item item)
{
item.Quality = ClampQuality(item.Quality - 2);
item.SellIn -= 1;
if (item.SellIn < 0)
{
item.Quality = ClampQuality(item.Quality - 2);
}
}
}

View File

@ -1,89 +1,25 @@
using System.Collections.Generic;
using System.Collections.Generic;
namespace GildedRoseKata;
public class GildedRose
{
IList<Item> Items;
private readonly IList<Item> _items;
private readonly IItemUpdaterFactory _factory;
public GildedRose(IList<Item> Items)
public GildedRose(IList<Item> items) : this(items, ItemUpdaterFactory.Default())
{
this.Items = Items;
}
public GildedRose(IList<Item> items, IItemUpdaterFactory factory)
{
_items = items;
_factory = factory;
}
public void UpdateQuality()
{
for (var i = 0; i < Items.Count; i++)
{
if (Items[i].Name != "Aged Brie" && Items[i].Name != "Backstage passes to a TAFKAL80ETC concert")
{
if (Items[i].Quality > 0)
{
if (Items[i].Name != "Sulfuras, Hand of Ragnaros")
{
Items[i].Quality = Items[i].Quality - 1;
}
}
}
else
{
if (Items[i].Quality < 50)
{
Items[i].Quality = Items[i].Quality + 1;
if (Items[i].Name == "Backstage passes to a TAFKAL80ETC concert")
{
if (Items[i].SellIn < 11)
{
if (Items[i].Quality < 50)
{
Items[i].Quality = Items[i].Quality + 1;
}
}
if (Items[i].SellIn < 6)
{
if (Items[i].Quality < 50)
{
Items[i].Quality = Items[i].Quality + 1;
}
}
}
}
}
if (Items[i].Name != "Sulfuras, Hand of Ragnaros")
{
Items[i].SellIn = Items[i].SellIn - 1;
}
if (Items[i].SellIn < 0)
{
if (Items[i].Name != "Aged Brie")
{
if (Items[i].Name != "Backstage passes to a TAFKAL80ETC concert")
{
if (Items[i].Quality > 0)
{
if (Items[i].Name != "Sulfuras, Hand of Ragnaros")
{
Items[i].Quality = Items[i].Quality - 1;
}
}
}
else
{
Items[i].Quality = Items[i].Quality - Items[i].Quality;
}
}
else
{
if (Items[i].Quality < 50)
{
Items[i].Quality = Items[i].Quality + 1;
}
}
}
}
foreach (var item in _items)
_factory.Get(item).Update(item);
}
}
}

View File

@ -0,0 +1,10 @@
namespace GildedRoseKata;
public interface IItemUpdater
{
/// <summary>The exact item name this strategy handles ("default" for the fallback).</summary>
string Name { get; }
/// <summary>Applies one full day: quality change + SellIn change + clamping.</summary>
void Update(Item item);
}

View File

@ -0,0 +1,6 @@
namespace GildedRoseKata;
public interface IItemUpdaterFactory
{
IItemUpdater Get(Item item);
}

View File

@ -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);
}

View File

@ -0,0 +1,52 @@
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<string, IItemUpdater> _updatersByName;
private readonly IItemUpdater _fallback;
public ItemUpdaterFactory(IEnumerable<IItemUpdater> updaters)
{
_updatersByName = new Dictionary<string, IItemUpdater>(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(),
new ConjuredItemUpdater(),
});
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;
}
}

View File

@ -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);
}
}
}

View File

@ -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.
}
}

View File

@ -0,0 +1,6 @@
[
{
Name: foo,
SellIn: -1
}
]

View File

@ -21,7 +21,7 @@ Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 14, 21
Backstage passes to a TAFKAL80ETC concert, 9, 50
Backstage passes to a TAFKAL80ETC concert, 4, 50
Conjured Mana Cake, 2, 5
Conjured Mana Cake, 2, 4
-------- day 2 --------
name, sellIn, quality
@ -33,7 +33,7 @@ Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 13, 22
Backstage passes to a TAFKAL80ETC concert, 8, 50
Backstage passes to a TAFKAL80ETC concert, 3, 50
Conjured Mana Cake, 1, 4
Conjured Mana Cake, 1, 2
-------- day 3 --------
name, sellIn, quality
@ -45,7 +45,7 @@ Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 12, 23
Backstage passes to a TAFKAL80ETC concert, 7, 50
Backstage passes to a TAFKAL80ETC concert, 2, 50
Conjured Mana Cake, 0, 3
Conjured Mana Cake, 0, 0
-------- day 4 --------
name, sellIn, quality
@ -57,7 +57,7 @@ Sulfuras, Hand of Ragnaros, -1, 80
Backstage passes to a TAFKAL80ETC concert, 11, 24
Backstage passes to a TAFKAL80ETC concert, 6, 50
Backstage passes to a TAFKAL80ETC concert, 1, 50
Conjured Mana Cake, -1, 1
Conjured Mana Cake, -1, 0
-------- day 5 --------
name, sellIn, quality

View File

@ -1,4 +1,4 @@
using Xunit;
using Xunit;
using System.Collections.Generic;
using GildedRoseKata;
@ -6,12 +6,193 @@ namespace GildedRoseTests;
public class GildedRoseTest
{
[Fact]
public void foo()
private const string AgedBrie = "Aged Brie";
private const string Sulfuras = "Sulfuras, Hand of Ragnaros";
private const string BackstagePass = "Backstage passes to a TAFKAL80ETC concert";
private static Item UpdateOne(string name, int sellIn, int quality)
{
IList<Item> Items = new List<Item> { new Item { Name = "foo", SellIn = 0, Quality = 0 } };
GildedRose app = new GildedRose(Items);
var item = new Item { Name = name, SellIn = sellIn, Quality = quality };
var app = new GildedRose(new List<Item> { item });
app.UpdateQuality();
Assert.Equal("fixme", Items[0].Name);
return item;
}
}
[Fact]
public void UpdateQuality_DoesNotChangeItemName()
{
var item = UpdateOne("foo", 0, 0);
Assert.Equal("foo", item.Name);
}
// ---------- Normal items ----------
[Fact]
public void NormalItem_BeforeSellBy_QualityDropsByOne()
{
var item = UpdateOne("+5 Dexterity Vest", 10, 20);
Assert.Equal(9, item.SellIn);
Assert.Equal(19, item.Quality);
}
[Fact]
public void NormalItem_OnSellByDate_QualityDropsByTwo()
{
var item = UpdateOne("+5 Dexterity Vest", 0, 20);
Assert.Equal(-1, item.SellIn);
Assert.Equal(18, item.Quality);
}
[Fact]
public void NormalItem_AfterSellBy_QualityDropsByTwo()
{
var item = UpdateOne("+5 Dexterity Vest", -1, 20);
Assert.Equal(-2, item.SellIn);
Assert.Equal(18, item.Quality);
}
[Fact]
public void NormalItem_QualityNeverNegative()
{
var item = UpdateOne("+5 Dexterity Vest", 5, 0);
Assert.Equal(0, item.Quality);
}
[Fact]
public void NormalItem_AfterSellByWithQualityOne_QualityStopsAtZero()
{
var item = UpdateOne("+5 Dexterity Vest", 0, 1);
Assert.Equal(0, item.Quality);
}
// ---------- Aged Brie ----------
[Fact]
public void AgedBrie_BeforeSellBy_QualityIncreasesByOne()
{
var item = UpdateOne(AgedBrie, 2, 0);
Assert.Equal(1, item.SellIn);
Assert.Equal(1, item.Quality);
}
[Fact]
public void AgedBrie_AfterSellBy_QualityIncreasesByTwo()
{
var item = UpdateOne(AgedBrie, 0, 10);
Assert.Equal(-1, item.SellIn);
Assert.Equal(12, item.Quality);
}
[Fact]
public void AgedBrie_QualityNeverExceedsFifty()
{
var item = UpdateOne(AgedBrie, 5, 50);
Assert.Equal(50, item.Quality);
}
[Fact]
public void AgedBrie_AfterSellByAtFortyNine_QualityCapsAtFifty()
{
var item = UpdateOne(AgedBrie, 0, 49);
Assert.Equal(50, item.Quality);
}
// ---------- Sulfuras ----------
[Fact]
public void Sulfuras_AtSellInZero_NeverChanges()
{
var item = UpdateOne(Sulfuras, 0, 80);
Assert.Equal(0, item.SellIn);
Assert.Equal(80, item.Quality);
}
[Fact]
public void Sulfuras_AtSellInMinusOne_NeverChanges()
{
var item = UpdateOne(Sulfuras, -1, 80);
Assert.Equal(-1, item.SellIn);
Assert.Equal(80, item.Quality);
}
// ---------- Backstage passes ----------
[Fact]
public void BackstagePass_MoreThanTenDays_QualityIncreasesByOne()
{
var item = UpdateOne(BackstagePass, 11, 20);
Assert.Equal(10, item.SellIn);
Assert.Equal(21, item.Quality);
}
[Fact]
public void BackstagePass_TenDaysOrLess_QualityIncreasesByTwo()
{
var item = UpdateOne(BackstagePass, 10, 20);
Assert.Equal(9, item.SellIn);
Assert.Equal(22, item.Quality);
}
[Fact]
public void BackstagePass_SixDays_QualityIncreasesByTwo()
{
var item = UpdateOne(BackstagePass, 6, 20);
Assert.Equal(22, item.Quality);
}
[Fact]
public void BackstagePass_FiveDaysOrLess_QualityIncreasesByThree()
{
var item = UpdateOne(BackstagePass, 5, 20);
Assert.Equal(4, item.SellIn);
Assert.Equal(23, item.Quality);
}
[Fact]
public void BackstagePass_AfterConcert_QualityDropsToZero()
{
var item = UpdateOne(BackstagePass, 0, 20);
Assert.Equal(-1, item.SellIn);
Assert.Equal(0, item.Quality);
}
[Fact]
public void BackstagePass_AtFortyNineCloseToConcert_QualityCapsAtFifty()
{
var item = UpdateOne(BackstagePass, 5, 49);
Assert.Equal(50, item.Quality);
}
// ---------- Conjured items ----------
[Fact]
public void ConjuredItem_BeforeSellBy_QualityDropsByTwo()
{
var item = UpdateOne("Conjured Mana Cake", 3, 6);
Assert.Equal(2, item.SellIn);
Assert.Equal(4, item.Quality);
}
[Fact]
public void ConjuredItem_OnSellByDate_QualityDropsByFour()
{
var item = UpdateOne("Conjured Mana Cake", 0, 6);
Assert.Equal(-1, item.SellIn);
Assert.Equal(2, item.Quality);
}
[Fact]
public void ConjuredItem_AfterSellBy_QualityDropsByFour()
{
var item = UpdateOne("Conjured Mana Cake", -1, 6);
Assert.Equal(-2, item.SellIn);
Assert.Equal(2, item.Quality);
}
[Fact]
public void ConjuredItem_QualityNeverNegative()
{
var item = UpdateOne("Conjured Mana Cake", 0, 3);
Assert.Equal(0, item.Quality);
}
}