From 0825883bee78ef5cfa01a52d34b5511d582a8701 Mon Sep 17 00:00:00 2001 From: "stanislaw.rusnak" Date: Tue, 3 Jun 2025 18:53:12 +0200 Subject: [PATCH] Refactoring GildedRose app code. Rebuild main logic with factory pattern. Add items specific classes. Extract constants. Optimize logic. Add unit tests. Adapt kata configs to work with windows env and java 21. --- .../java/com/gildedrose/AgedBrieItem.java | 13 ++ .../com/gildedrose/BackstagePassItem.java | 32 +++++ .../java/com/gildedrose/ConjuredItem.java | 14 +++ .../main/java/com/gildedrose/GildedRose.java | 68 +++------- .../main/java/com/gildedrose/RegularItem.java | 19 +++ .../java/com/gildedrose/SulfurasItem.java | 14 +++ .../java/com/gildedrose/UpdatableItem.java | 41 ++++++ .../java/com/gildedrose/GildedRoseTest.java | 118 +++++++++++++++++- start_texttest.sh | 4 +- texttests/config.gr | 4 +- 10 files changed, 271 insertions(+), 56 deletions(-) create mode 100644 Java/src/main/java/com/gildedrose/AgedBrieItem.java create mode 100644 Java/src/main/java/com/gildedrose/BackstagePassItem.java create mode 100644 Java/src/main/java/com/gildedrose/ConjuredItem.java create mode 100644 Java/src/main/java/com/gildedrose/RegularItem.java create mode 100644 Java/src/main/java/com/gildedrose/SulfurasItem.java create mode 100644 Java/src/main/java/com/gildedrose/UpdatableItem.java diff --git a/Java/src/main/java/com/gildedrose/AgedBrieItem.java b/Java/src/main/java/com/gildedrose/AgedBrieItem.java new file mode 100644 index 00000000..46b3131b --- /dev/null +++ b/Java/src/main/java/com/gildedrose/AgedBrieItem.java @@ -0,0 +1,13 @@ +package com.gildedrose; + +class AgedBrieItem extends UpdatableItem { + public AgedBrieItem(Item item) { + super(item); + } + + @Override + public void update() { + incrementQuality(STANDARD_VALUE_CHANGE); + decrementSellIn(); + } +} diff --git a/Java/src/main/java/com/gildedrose/BackstagePassItem.java b/Java/src/main/java/com/gildedrose/BackstagePassItem.java new file mode 100644 index 00000000..c1fa1036 --- /dev/null +++ b/Java/src/main/java/com/gildedrose/BackstagePassItem.java @@ -0,0 +1,32 @@ +package com.gildedrose; + +class BackstagePassItem extends UpdatableItem { + private static final int FIRST_SELL_IN_THRESHOLD = 10; + private static final int SECOND_SELL_IN_THRESHOLD = 5; + + public BackstagePassItem(Item item) { + super(item); + } + + @Override + public void update() { + decrementSellIn(); + updateQuality(); + } + + private void updateQuality() { + if (isOutdated()) { + setZeroQuality(); + } else { + incrementQuality(getIncrementValue()); + } + } + + private int getIncrementValue() { + int increment = STANDARD_VALUE_CHANGE; + if (item.sellIn <= FIRST_SELL_IN_THRESHOLD) increment++; + if (item.sellIn <= SECOND_SELL_IN_THRESHOLD) increment++; + return increment; + } +} + diff --git a/Java/src/main/java/com/gildedrose/ConjuredItem.java b/Java/src/main/java/com/gildedrose/ConjuredItem.java new file mode 100644 index 00000000..092ba451 --- /dev/null +++ b/Java/src/main/java/com/gildedrose/ConjuredItem.java @@ -0,0 +1,14 @@ +package com.gildedrose; + +class ConjuredItem extends UpdatableItem { + + public ConjuredItem(Item item) { + super(item); + } + + @Override + public void update() { + decrementQuality(DOUBLE_VALUE_CHANGE); + decrementSellIn(); + } +} diff --git a/Java/src/main/java/com/gildedrose/GildedRose.java b/Java/src/main/java/com/gildedrose/GildedRose.java index 87a3b926..5e358465 100644 --- a/Java/src/main/java/com/gildedrose/GildedRose.java +++ b/Java/src/main/java/com/gildedrose/GildedRose.java @@ -1,6 +1,13 @@ package com.gildedrose; +import java.util.Arrays; + class GildedRose { + private static final String BACKSTAGE_PASS_NAME = "Backstage passes to a TAFKAL80ETC concert"; + private static final String SULFURAS_NAME = "Sulfuras, Hand of Ragnaros"; + private static final String AGED_BRIE_NAME = "Aged Brie"; + private static final String CONJURED_NAME = "Conjured"; + Item[] items; public GildedRose(Item[] items) { @@ -8,55 +15,18 @@ class GildedRose { } 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; + Arrays.stream(items) + .map(this::mapToUpdatableItem) + .forEach(UpdatableItem::update); + } - 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; - } - } - } - } + private UpdatableItem mapToUpdatableItem(Item item) { + return switch (item.name) { + case AGED_BRIE_NAME -> new AgedBrieItem(item); + case BACKSTAGE_PASS_NAME -> new BackstagePassItem(item); + case SULFURAS_NAME -> new SulfurasItem(item); + case CONJURED_NAME -> new ConjuredItem(item); + default -> new RegularItem(item); + }; } } diff --git a/Java/src/main/java/com/gildedrose/RegularItem.java b/Java/src/main/java/com/gildedrose/RegularItem.java new file mode 100644 index 00000000..4732c9f1 --- /dev/null +++ b/Java/src/main/java/com/gildedrose/RegularItem.java @@ -0,0 +1,19 @@ +package com.gildedrose; + +class RegularItem extends UpdatableItem { + public RegularItem(Item item) { + super(item); + } + + @Override + public void update() { + decrementSellIn(); + updateQuality(); + } + + private void updateQuality() { + int decrementAmount = isOutdated() ? DOUBLE_VALUE_CHANGE : STANDARD_VALUE_CHANGE; + decrementQuality(decrementAmount); + } +} + diff --git a/Java/src/main/java/com/gildedrose/SulfurasItem.java b/Java/src/main/java/com/gildedrose/SulfurasItem.java new file mode 100644 index 00000000..d9ea53b2 --- /dev/null +++ b/Java/src/main/java/com/gildedrose/SulfurasItem.java @@ -0,0 +1,14 @@ +package com.gildedrose; + +class SulfurasItem extends UpdatableItem { + public static final int SULFURAS_CONSTANT_QUALITY = 80; + + public SulfurasItem(Item item) { + super(item); + item.quality = SULFURAS_CONSTANT_QUALITY; + } + + @Override + public void update() {} +} + diff --git a/Java/src/main/java/com/gildedrose/UpdatableItem.java b/Java/src/main/java/com/gildedrose/UpdatableItem.java new file mode 100644 index 00000000..f1b97302 --- /dev/null +++ b/Java/src/main/java/com/gildedrose/UpdatableItem.java @@ -0,0 +1,41 @@ +package com.gildedrose; + +import static java.lang.Math.max; +import static java.lang.Math.min; + +abstract class UpdatableItem { + protected static final int DOUBLE_VALUE_CHANGE = 2; + protected static final int STANDARD_VALUE_CHANGE = 1; + protected static final int QUALITY_MAX = 50; + protected static final int QUALITY_MIN = 0; + protected static final int ZERO_SELL_IN = 0; + + protected Item item; + + protected UpdatableItem(Item item) { + this.item = item; + } + + public abstract void update(); + + protected void incrementQuality(int incrementAmount) { + item.quality = min(QUALITY_MAX, item.quality + incrementAmount); + } + + protected void setZeroQuality() { + item.quality = QUALITY_MIN; + } + + protected void decrementQuality(int decrementAmount) { + item.quality = max(QUALITY_MIN, item.quality - decrementAmount); + } + + protected void decrementSellIn() { + item.sellIn--; + } + + protected boolean isOutdated() { + return item.sellIn < ZERO_SELL_IN; + } +} + diff --git a/Java/src/test/java/com/gildedrose/GildedRoseTest.java b/Java/src/test/java/com/gildedrose/GildedRoseTest.java index 8ae29eec..8803a2c4 100644 --- a/Java/src/test/java/com/gildedrose/GildedRoseTest.java +++ b/Java/src/test/java/com/gildedrose/GildedRoseTest.java @@ -7,11 +7,123 @@ import static org.junit.jupiter.api.Assertions.assertEquals; class GildedRoseTest { @Test - void foo() { - Item[] items = new Item[] { new Item("foo", 0, 0) }; + void shouldDefaultDecreaseQualityBeforeSellIn() { + Item[] items = new Item[]{ + new Item("testProductName", 9, 30) + }; GildedRose app = new GildedRose(items); app.updateQuality(); - assertEquals("fixme", app.items[0].name); + assertEquals("testProductName", app.items[0].name); + assertEquals(29, app.items[0].quality); + } + + @Test + void shouldDoubleDecreaseQualityAfterSellIn() { + Item[] items = new Item[]{ + new Item("testProductName", -2, 30) + }; + GildedRose app = new GildedRose(items); + app.updateQuality(); + assertEquals("testProductName", app.items[0].name); + assertEquals(28, app.items[0].quality); + } + + @Test + void shouldNotDecreaseQualityBelowZero() { + Item[] items = new Item[]{ + new Item("testProductName", 9, 0) + }; + GildedRose app = new GildedRose(items); + app.updateQuality(); + assertEquals("testProductName", app.items[0].name); + assertEquals(0, app.items[0].quality); + } + + @Test + void shouldIncreaseQualityOfAgedBrie() { + Item[] items = new Item[]{ + new Item("Aged Brie", 9, 30) + }; + GildedRose app = new GildedRose(items); + app.updateQuality(); + assertEquals("Aged Brie", app.items[0].name); + assertEquals(31, app.items[0].quality); + } + + @Test + void shouldNotIncreaseQualityAboveFifty() { + Item[] items = new Item[]{ + new Item("Aged Brie", 9, 50) + }; + GildedRose app = new GildedRose(items); + app.updateQuality(); + assertEquals("Aged Brie", app.items[0].name); + assertEquals(50, app.items[0].quality); + } + + @Test + void shouldNotChangeSulfurasQuality() { + Item[] items = new Item[]{ + new Item("Sulfuras, Hand of Ragnaros", 9, 80) + }; + GildedRose app = new GildedRose(items); + app.updateQuality(); + assertEquals("Sulfuras, Hand of Ragnaros", app.items[0].name); + assertEquals(80, app.items[0].quality); + } + + @Test + void shouldIncreaseQualityBy2Below10DaysForSpecialProducts() { + Item[] items = new Item[]{ + new Item("Backstage passes to a TAFKAL80ETC concert", 9, 32) + }; + GildedRose app = new GildedRose(items); + app.updateQuality(); + assertEquals("Backstage passes to a TAFKAL80ETC concert", app.items[0].name); + assertEquals(34, app.items[0].quality); + } + + @Test + void shouldIncreaseQualityBy3AfterBelow5DaysForSpecialProducts() { + Item[] items = new Item[]{ + new Item("Backstage passes to a TAFKAL80ETC concert", 4, 32) + }; + GildedRose app = new GildedRose(items); + app.updateQuality(); + assertEquals("Backstage passes to a TAFKAL80ETC concert", app.items[0].name); + assertEquals(35, app.items[0].quality); + } + + @Test + void shouldSetZeroQualityForSpecialProductsAfterSellIn() { + Item[] items = new Item[]{ + new Item("Backstage passes to a TAFKAL80ETC concert", -2, 32) + }; + GildedRose app = new GildedRose(items); + app.updateQuality(); + assertEquals("Backstage passes to a TAFKAL80ETC concert", app.items[0].name); + assertEquals(0, app.items[0].quality); + } + + @Test + void shouldDoubleDecreaseQualityForConjuredItems() { + Item[] items = new Item[]{ + new Item("Conjured", 9, 30) + }; + GildedRose app = new GildedRose(items); + app.updateQuality(); + assertEquals("Conjured", app.items[0].name); + assertEquals(28, app.items[0].quality); + } + + @Test + void shouldNotExceedQualityLimitForBackstageItem() { + Item[] items = new Item[]{ + new Item("Backstage passes to a TAFKAL80ETC concert", 4, 49) + }; + GildedRose app = new GildedRose(items); + app.updateQuality(); + assertEquals(50, app.items[0].quality); } } diff --git a/start_texttest.sh b/start_texttest.sh index 805922b1..e1b9cdd1 100755 --- a/start_texttest.sh +++ b/start_texttest.sh @@ -3,5 +3,5 @@ if [ ! -d "venv" ]; then python -m venv venv fi -venv/bin/pip install texttest -venv/bin/texttest -d . -con "$@" +venv/Scripts/pip.exe install texttest +venv/Scripts/texttest.exe -d . -con "$@" diff --git a/texttests/config.gr b/texttests/config.gr index 9c707ffc..d0137087 100755 --- a/texttests/config.gr +++ b/texttests/config.gr @@ -18,8 +18,8 @@ diff_program:meld #executable:${TEXTTEST_HOME}/zig/zig-out/bin/zig # Settings for the Java version using Gradle wrapped in a python script -#executable:${TEXTTEST_HOME}/Java/texttest_rig.py -#interpreter:python +executable:${TEXTTEST_HOME}/Java/texttest_rig.py +interpreter:python # Settings for the Java version using the classpath #executable:com.gildedrose.TexttestFixture