diff --git a/Java/src/main/java/com/gildedrose/Category.java b/Java/src/main/java/com/gildedrose/Category.java new file mode 100644 index 00000000..940b5f72 --- /dev/null +++ b/Java/src/main/java/com/gildedrose/Category.java @@ -0,0 +1,27 @@ +package com.gildedrose; + +import java.util.function.ToIntFunction; + +public class Category { + + private final String name; + private ToIntFunction updateQualityFunction; + + public Category(String name) { + this.name = name; + } + + public Category withQualityUpdater(ToIntFunction updateQualityFunction) { + this.updateQualityFunction = updateQualityFunction; + return this; + } + + public boolean isItemMyCategory(String itemName) { + return itemName != null && itemName.toLowerCase().startsWith(name.toLowerCase()); + } + + public void updateItem(Item item) { + item.sellIn--; + item.quality = Math.max(0, Math.min(50, updateQualityFunction.applyAsInt(item))); + } +} diff --git a/Java/src/main/java/com/gildedrose/DefaultCategory.java b/Java/src/main/java/com/gildedrose/DefaultCategory.java new file mode 100644 index 00000000..4d533294 --- /dev/null +++ b/Java/src/main/java/com/gildedrose/DefaultCategory.java @@ -0,0 +1,9 @@ +package com.gildedrose; + +public class DefaultCategory extends Category { + + public DefaultCategory() { + super(null); + withQualityUpdater(new StandardQualityDegrader()); + } +} diff --git a/Java/src/main/java/com/gildedrose/GildedRose.java b/Java/src/main/java/com/gildedrose/GildedRose.java index e6feb751..6f2de05e 100644 --- a/Java/src/main/java/com/gildedrose/GildedRose.java +++ b/Java/src/main/java/com/gildedrose/GildedRose.java @@ -3,60 +3,13 @@ package com.gildedrose; class GildedRose { Item[] items; + private final QualityUpdater qualityUpdater = new QualityUpdater(); + public GildedRose(Item[] items) { this.items = items; } public void updateQuality() { - for (int i = 0; i < items.length; i++) { - if (!items[i].name.equals("Aged Brie") - && !items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { - if (items[i].quality > 0) { - if (!items[i].name.equals("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.equals("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.equals("Sulfuras, Hand of Ragnaros")) { - items[i].sellIn = items[i].sellIn - 1; - } - - if (items[i].sellIn < 0) { - if (!items[i].name.equals("Aged Brie")) { - if (!items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { - if (items[i].quality > 0) { - if (!items[i].name.equals("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; - } - } - } - } + qualityUpdater.updateQuality(items); } -} \ No newline at end of file +} diff --git a/Java/src/main/java/com/gildedrose/LegendaryCategory.java b/Java/src/main/java/com/gildedrose/LegendaryCategory.java new file mode 100644 index 00000000..536fb591 --- /dev/null +++ b/Java/src/main/java/com/gildedrose/LegendaryCategory.java @@ -0,0 +1,13 @@ +package com.gildedrose; + +public class LegendaryCategory extends Category { + + public LegendaryCategory(String name) { + super(name); + } + + @Override + public void updateItem(Item item) { + // Legendary Items never alter + } +} diff --git a/Java/src/main/java/com/gildedrose/QualityUpdater.java b/Java/src/main/java/com/gildedrose/QualityUpdater.java new file mode 100644 index 00000000..39aabf53 --- /dev/null +++ b/Java/src/main/java/com/gildedrose/QualityUpdater.java @@ -0,0 +1,55 @@ +package com.gildedrose; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class QualityUpdater { + + // The 'categories' form the actual quality update specifications: + private final Category[] categories = new Category[] { + // "Aged Brie" actually increases in Quality the older it gets: + new Category("Aged Brie") + .withQualityUpdater(item -> item.quality + 1), + + // "Backstage passes", like aged brie, increases in Quality as its SellIn value approaches. + // Quality increases by 2 when there are 10 days or less and by 3 when there are 5 days or less but + // Quality drops to 0 after the concert: + new Category("Backstage passes") + .withQualityUpdater(item -> { + if (item.sellIn < 0) { + return 0; + } + if (item.sellIn < 5) { + return item.quality + 3; + } + if (item.sellIn < 10) { + return item.quality + 2; + } + return item.quality + 1; + }), + + // "Sulfuras", being a legendary item, never has to be sold or decreases in Quality: + new LegendaryCategory("Sulfuras"), + + // "Conjured" items degrade in Quality twice as fast as normal items: + new Category("Conjured") + .withQualityUpdater(new StandardQualityDegrader(2)) + }; + + // Maps an Item name to a specific Category, + // only to improve `findCategory()` in terms of speed: + private final Map categoryByItemName = new HashMap<>(); + + public void updateQuality(Item[] items) { + Arrays.stream(items) + .forEach(item -> categoryByItemName.computeIfAbsent(item.name, this::findCategory).updateItem(item)); + } + + private Category findCategory(String itemName) { + return Arrays.stream(categories) + .filter(category -> category.isItemMyCategory(itemName)) + .findFirst() + .orElseGet(DefaultCategory::new); + } +} diff --git a/Java/src/main/java/com/gildedrose/StandardQualityDegrader.java b/Java/src/main/java/com/gildedrose/StandardQualityDegrader.java new file mode 100644 index 00000000..c07bdca6 --- /dev/null +++ b/Java/src/main/java/com/gildedrose/StandardQualityDegrader.java @@ -0,0 +1,22 @@ +package com.gildedrose; + +import java.util.function.ToIntFunction; + +public class StandardQualityDegrader implements ToIntFunction { + + private final int speed; + + public StandardQualityDegrader() { + this(1); + } + + public StandardQualityDegrader(int speed) { + this.speed = speed; + } + + @Override + public int applyAsInt(Item item) { + // Once the sell by date has passed, Quality degrades twice as fast: + return item.sellIn >= 0 ? item.quality - speed : item.quality - speed * 2; + } +} diff --git a/Java/src/test/java/com/gildedrose/GildedRoseTest.java b/Java/src/test/java/com/gildedrose/GildedRoseTest.java index 8ae29eec..fc3c91c6 100644 --- a/Java/src/test/java/com/gildedrose/GildedRoseTest.java +++ b/Java/src/test/java/com/gildedrose/GildedRoseTest.java @@ -2,16 +2,118 @@ package com.gildedrose; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; class GildedRoseTest { @Test - void foo() { - Item[] items = new Item[] { new Item("foo", 0, 0) }; + void testAgedBrieItems() { + Item[] items = new Item[] { + new Item("Aged Brie", 100, 10), + new Item("Aged Brie", 10, 40), + new Item("Aged Brie", 2, 49), + new Item("Aged Brie", 1, 50), + }; + GildedRose app = new GildedRose(items); app.updateQuality(); - assertEquals("fixme", app.items[0].name); + + assertItem(items[0], "Aged Brie", 99, 11); + assertItem(items[1], "Aged Brie", 9, 41); + assertItem(items[2], "Aged Brie", 1, 50); + assertItem(items[3], "Aged Brie", 0, 50); } + @Test + void testSulfurasItems() { + Item[] items = new Item[] { + new Item("Sulfuras, Hand of Ragnaros", 100, 80), + new Item("Sulfuras, Hand of Ragnaros", -1, 80), + }; + + GildedRose app = new GildedRose(items); + app.updateQuality(); + + assertItem(items[0], "Sulfuras, Hand of Ragnaros", 100, 80); + assertItem(items[1], "Sulfuras, Hand of Ragnaros", -1, 80); + } + + @Test + void testBackstagePassesItems() { + Item[] items = new Item[] { + new Item("Backstage passes to a TAFKAL80ETC concert", 11, 5), + new Item("Backstage passes to a TAFKAL80ETC concert", 10, 5), + new Item("Backstage passes to a TAFKAL80ETC concert", 9, 5), + new Item("Backstage passes to a TAFKAL80ETC concert", 6, 5), + new Item("Backstage passes to a TAFKAL80ETC concert", 5, 5), + new Item("Backstage passes to a TAFKAL80ETC concert", 4, 5), + new Item("Backstage passes to a TAFKAL80ETC concert", 1, 5), + new Item("Backstage passes to a TAFKAL80ETC concert", 0, 5), + }; + + GildedRose app = new GildedRose(items); + app.updateQuality(); + + assertItem(items[0], "Backstage passes to a TAFKAL80ETC concert", 10, 6); + assertItem(items[1], "Backstage passes to a TAFKAL80ETC concert", 9, 7); + assertItem(items[2], "Backstage passes to a TAFKAL80ETC concert", 8, 7); + assertItem(items[3], "Backstage passes to a TAFKAL80ETC concert", 5, 7); + assertItem(items[4], "Backstage passes to a TAFKAL80ETC concert", 4, 8); + assertItem(items[5], "Backstage passes to a TAFKAL80ETC concert", 3, 8); + assertItem(items[6], "Backstage passes to a TAFKAL80ETC concert", 0, 8); + assertItem(items[7], "Backstage passes to a TAFKAL80ETC concert", -1, 0); + } + + @Test + void testStandardItems() { + Item[] items = new Item[] { + new Item("NoParticularName", 25, 30), + new Item("NoParticularName", 1, 30), + new Item("NoParticularName", 0, 30), + new Item("NoParticularName", -1, 30), + new Item("NoParticularName", 1, 1), + new Item("NoParticularName", 0, 0), + }; + + GildedRose app = new GildedRose(items); + app.updateQuality(); + + assertItem(items[0], "NoParticularName", 24, 29); + assertItem(items[1], "NoParticularName", 0, 29); + assertItem(items[2], "NoParticularName", -1, 28); + assertItem(items[3], "NoParticularName", -2, 28); + assertItem(items[4], "NoParticularName", 0, 0); + assertItem(items[5], "NoParticularName", -1, 0); + } + + @Test + void testConjuredItems() { + Item[] items = new Item[] { + new Item("Conjured Mana Cake", 25, 30), + new Item("Conjured Mana Cake", 1, 30), + new Item("Conjured Mana Cake", 0, 30), + new Item("Conjured Mana Cake", -1, 30), + new Item("Conjured Mana Cake", 1, 1), + new Item("Conjured Mana Cake", 0, 0), + }; + + GildedRose app = new GildedRose(items); + app.updateQuality(); + + assertItem(items[0], "Conjured Mana Cake", 24, 28); + assertItem(items[1], "Conjured Mana Cake", 0, 28); + assertItem(items[2], "Conjured Mana Cake", -1, 26); + assertItem(items[3], "Conjured Mana Cake", -2, 26); + assertItem(items[4], "Conjured Mana Cake", 0, 0); + assertItem(items[5], "Conjured Mana Cake", -1, 0); + } + + private void assertItem(Item item, String name, int sellIn, int quality) { + assertAll( + () -> assertEquals(name, item.name, "item name"), + () -> assertEquals(sellIn, item.sellIn, "item sellIn"), + () -> assertEquals(quality, item.quality, "item quality") + ); + } }